<template>
  <view class="chat-container">
    <!-- 顶部导航栏 - 固定定位 -->
    <view class="chat-header">
      <view class="header-left">
        <view class="back-btn" @click="goBack">
          <FeatherIcon name="chevron-left" size="24" color="#333" />
        </view>
        <text class="node-name">摸鱼派聊天室-{{ nodeName }}</text>
      </view>
      <view
        class="connection-badge"
        :class="connectionStatusClass"
        @click="manualReconnect"
      >
        <text>{{ connectionStatusText }}</text>
        <FeatherIcon
          v-if="connectionStatus === 'disconnected'"
          name="refresh-cw"
          size="16"
          color="#ff4d4f"
        />
      </view>
      <view class="online-count" @click="toggleUserList">
        <text>在线: {{ onlineCount }}</text>
        <FeatherIcon name="chevron-down" size="16" color="#666" />
      </view>
    </view>

    <!-- 在线用户列表弹窗 -->
    <view
      class="user-list-modal"
      v-if="showUserList"
      @click.self="toggleUserList"
    >
      <view class="user-list-container">
        <view class="user-list-header">
          <text class="user-list-title"
            >在线用户 ({{ onlineUsers.length }})</text
          >
          <view class="online-close-btn" @click="toggleUserList">
            <FeatherIcon name="x" size="24" color="#666" />
          </view>
        </view>
        <view class="user-grid">
          <view
            class="user-avatar-item"
            v-for="(user, index) in onlineUsers"
            :key="index"
            @click="openUserHomePage(user.homePage)"
          >
            <image
              class="user-avatar"
              :src="user.userAvatarURL48"
              mode="aspectFill"
            ></image>
          </view>
        </view>
      </view>
    </view>

    <!-- 聊天内容区域 - 添加顶部和底部内边距 -->
    <view
      class="chat-content"
      :style="
        keyboardHeight > 0
          ? {
              marginBottom: keyboardHeight + 100 + 'px', // 保留原来的底部边距加上键盘高度
              height: `calc(100vh - 330rpx)`, // 保持原高度计算，不减去键盘高度
            }
          : {
              marginBottom: '150rpx', // 默认底部边距
              height: `calc(100vh - 330rpx)`, // 默认高度
            }
      "
    >
      <!-- 聊天消息列表 -->
      <scroll-view
        class="chat-messages"
        scroll-y
        :scroll-top="scrollTop"
        @scrolltoupper="loadMoreMessages"
        :upper-threshold="100"
        :scroll-with-animation="true"
        @scroll="onScroll"
        :show-scrollbar="false"
      >
        <view class="loading-more" v-if="loading && page > 1">
          <view class="loading-dot"></view>
          <view class="loading-dot"></view>
          <view class="loading-dot"></view>
        </view>

        <view class="chat-list">
          <!-- 遍历消息列表 -->
          <block
            v-for="(message, index) in messages"
            :key="message.oId || index"
          >
            <!-- 日期分割线，当日期变化时显示 -->
            <view
              class="date-divider"
              v-if="
                index === 0 ||
                isDifferentDay(messages[index - 1].time, message.time)
              "
            >
              <text>{{ formatDateHeader(message.time) }}</text>
            </view>

            <!-- 聊天消息项 -->
            <view
              class="message-item"
              :class="{ 'self-message': isSelfMessage(message) }"
              :id="`msg-${message.oId || index}`"
            >
              <!-- 头像 - 所有消息都显示头像 -->
              <view
                class="avatar-container"
                @click="navigateToUserDetail(message.userName)"
              >
                <image
                  class="avatar"
                  :src="
                    isSelfMessage(message)
                      ? currentUserAvatar
                      : message.userAvatarURL ||
                        'https://file.fishpi.cn/default-avatar.png'
                  "
                  mode="aspectFill"
                  @longpress="atUser(message)"
                ></image>
              </view>

              <!-- 消息内容 -->
              <view class="message-content">
                <!-- 用户名 - 仅在他人消息顶部显示 -->
                <view class="username" v-if="!isSelfMessage(message)">
                  {{
                    message.userNickname + "(" + message.userName + ")" ||
                    "未知用户"
                  }}
                </view>

                <!-- 系统消息、红包通知或红包领取消息直接显示，不需要气泡 -->
                <view
                  v-if="
                    message._isSystemMessage ||
                    isRedPacketMessage(message) ||
                    message._isRedPacketNotice
                  "
                  @tap="handleMessageBubbleTap($event, message)"
                  :class="{
                    'redpacket-notice-msg': message._isRedPacketNotice,
                  }"
                >
                  <text v-if="message._isRedPacketNotice">{{
                    message.content
                  }}</text>
                  <rich-text
                    v-else
                    :nodes="renderMessageContent(message)"
                  ></rich-text>
                </view>

                <!-- 普通消息气泡 -->
                <view
                  v-else
                  class="message-bubble"
                  @longpress="showMessageMenu(message)"
                  @tap="handleMessageBubbleTap($event, message)"
                >
                  <!-- 文本消息 -->
                  <view
                    class="normal-message"
                    v-if="!isSpecialMessage(message)"
                  >
                    <rich-text
                      :nodes="renderMessageContent(message, true)"
                    ></rich-text>
                    <!-- <view class="message-images" v-if="hasImages(message)">
                      <view
                        v-for="(imageUrl, imgIndex) in getMessageImages(
                          message
                        )"
                        :key="imgIndex"
                        class="message-image-wrapper"
                        @tap.stop="
                          previewImage(imageUrl, getMessageImages(message))
                        "
                      >
                        <image
                          :src="imageUrl"
                          mode="widthFix"
                          class="message-image"
                        ></image>
                      </view>
                    </view> -->
                  </view>

                  <!-- 特殊消息（比如天气、系统通知等） -->
                  <view class="special-message" v-else>
                    <rich-text
                      :nodes="renderMessageContent(message)"
                    ></rich-text>
                  </view>

                  <!-- 消息底部信息：时间和客户端 -->
                  <view class="message-footer">
                    <text class="message-time">{{
                      formatMessageTime(message.time)
                    }}</text>
                    <text class="client-info" v-if="message.client"
                      >来自{{ message.client }}</text
                    >
                  </view>
                </view>
              </view>
            </view>
          </block>
        </view>

        <view class="no-more" v-if="!hasMore && messages.length > 0">
          <text>没有更多消息了</text>
        </view>

        <view class="empty-state" v-if="messages.length === 0 && !loading">
          <image
            src="/static/icons/empty-chat.png"
            mode="aspectFit"
            class="empty-icon"
          ></image>
          <text class="empty-text">暂无消息，快来聊天吧</text>
        </view>
      </scroll-view>

      <!-- 新消息提示 -->
      <view
        class="new-message-alert"
        v-if="hasNewMessage && userScrolled && !shouldAutoScroll"
        @click="goToLatestMessage"
      >
        <view class="alert-content">
          <view class="alert-icon">
            <FeatherIcon name="arrow-down" size="16" color="#fff" />
          </view>
          <text>{{ newMessageCount }}条新消息</text>
        </view>
      </view>
    </view>

    <!-- 底部输入区域 - 固定在底部 -->
    <view
      class="input-area"
      :style="{ bottom: keyboardHeight + 'px' }"
      v-if="isLoggedIn"
    >
      <!-- 当前话题显示在输入框上方 -->
      <view class="input-topic" v-if="currentTopic">
        <FeatherIcon name="hash" size="16" color="#1890ff" />
        <text>当前话题: {{ currentTopic }}</text>
      </view>
      <!-- 引用提示 -->
      <view class="quote-indicator" v-if="showQuoteIndicator">
        <text class="quote-text"
          >引用: @{{
            typeof window !== "undefined" && window._currentQuote
              ? window._currentQuote.userName
              : ""
          }}</text
        >
        <view class="quote-cancel" @tap="cancelQuote">
          <FeatherIcon name="x" size="16" color="#999" />
        </view>
      </view>

      <!-- 修改输入区域部分，将发送按钮替换为加号，并添加功能面板 -->
      <view class="input-wrapper">
        <view class="emoji-btn" @click="toggleEmojiPanel">
          <FeatherIcon
            :name="showEmojiPanel ? 'x-circle' : 'smile'"
            size="24"
            color="#666"
          />
        </view>

        <input
          class="message-input"
          type="text"
          v-model="messageContent"
          placeholder="说点什么..."
          confirm-type="send"
          :disabled="connectionStatus !== 'connected'"
          @confirm="sendMessage"
          @focus="handleInputFocus"
          @blur="handleInputBlur"
          :adjust-position="false"
        />

        <view
          class="action-btn"
          @click="messageContent.trim() ? sendMessage() : toggleMorePanel()"
        >
          <FeatherIcon
            :name="messageContent.trim() ? 'send' : 'plus'"
            size="24"
            :color="messageContent.trim() ? '#fff' : '#fff'"
          />
        </view>
      </view>

      <!-- 功能面板 -->
      <view class="more-panel" v-if="showMorePanel">
        <view class="more-item" @click="chooseImage">
          <view class="more-icon">
            <FeatherIcon name="image" size="28" color="#666" />
          </view>
          <text class="more-text">相册</text>
        </view>
        <view class="more-item" @click="takePhoto">
          <view class="more-icon">
            <FeatherIcon name="camera" size="28" color="#666" />
          </view>
          <text class="more-text">拍摄</text>
        </view>
        <view class="more-item" @click="openTail">
          <view class="more-icon">
            <FeatherIcon name="feather" size="28" color="#666" />
          </view>
          <text class="more-text">小尾巴</text>
        </view>
        <view class="more-item" @click="openRedPacket">
          <view class="more-icon">
            <FeatherIcon name="gift" size="28" color="#ff4d4f" />
          </view>
          <text class="more-text">红包</text>
        </view>
      </view>

      <!-- 表情面板 -->
      <view class="emoji-panel" v-if="showEmojiPanel">
        <view class="emoji-tabs">
          <view
            class="emoji-tab"
            :class="{ 'active-tab': activeEmojiTab === 'default' }"
            @click="switchToDefaultTab"
          >
            <text>默认表情</text>
          </view>
          <view
            class="emoji-tab"
            :class="{ 'active-tab': activeEmojiTab === 'custom' }"
            @click="switchToCustomTab"
          >
            <text>我的表情</text>
          </view>
        </view>

        <swiper
          class="emoji-swiper"
          :current="activeEmojiTab === 'default' ? 0 : 1"
          @change="handleEmojiTabChange"
        >
          <!-- 默认表情 -->
          <swiper-item>
            <scroll-view
              scroll-y
              class="emoji-scroll"
              v-if="defaultEmoticons.length > 0"
            >
              <view class="emoji-list">
                <view
                  class="emoji-item"
                  v-for="(emoji, index) in defaultEmoticons"
                  :key="'default-' + index"
                  @click="insertEmoji(emoji)"
                >
                  <text class="emoji-text" v-if="!isImageUrl(emoji)">{{
                    emoji
                  }}</text>
                  <image
                    v-else
                    class="emoji-image"
                    :src="emoji"
                    mode="aspectFit"
                  ></image>
                </view>
              </view>

              <!-- 空状态 -->
              <view
                class="emoji-empty"
                v-if="defaultEmoticons.length === 0 && !isLoadingEmoticons"
              >
                <text>暂无默认表情</text>
              </view>
            </scroll-view>
          </swiper-item>

          <!-- 用户表情包 -->
          <swiper-item>
            <scroll-view scroll-y class="emoji-scroll">
              <view class="emoji-list">
                <!-- 添加表情按钮放在开头 -->
                <view class="emoji-item add-emoji-item" @click="addCustomEmoji">
                  <view class="add-emoji-icon">
                    <FeatherIcon name="plus-square" size="40" color="#666" />
                  </view>
                </view>

                <view
                  class="emoji-item"
                  v-for="(emoji, index) in customEmoticons"
                  :key="'custom-' + index"
                  @click="insertEmoji(emoji)"
                  @longpress="handleLongPress(index)"
                >
                  <image
                    class="emoji-image"
                    :src="emoji"
                    mode="aspectFit"
                    :class="{
                      'emoji-delete-active': selectedEmojiIndex === index,
                    }"
                  ></image>
                  <view
                    class="emoji-delete-icon"
                    v-if="selectedEmojiIndex === index"
                    @click.stop="deleteEmoji(index)"
                  >
                    <FeatherIcon name="trash-2" size="24" color="#ff4d4f" />
                  </view>
                </view>
              </view>
            </scroll-view>
          </swiper-item>
        </swiper>

        <!-- 加载中 -->
        <view
          class="emoji-loading"
          v-if="isLoadingEmoticons || isLoadingCustomEmoticons"
        >
          <text>加载表情中...</text>
        </view>
      </view>
    </view>

    <!-- 未登录提示 -->
    <view class="login-prompt" v-else @click="goToLogin">
      <view class="login-content">
        <FeatherIcon name="lock" size="24" color="#007AFF"></FeatherIcon>
        <text>登录后参与聊天</text>
      </view>
    </view>

    <!-- 悬浮音乐播放器 -->
    <view class="floating-music-player" v-if="showMusicPlayer">
      <view class="music-player-container">
        <view class="music-player-cover" @click="toggleMusicPlayback">
          <image
            class="cover-image"
            :src="currentMusic.coverURL"
            :class="{ rotating: isPlaying }"
            mode="aspectFill"
          />
          <view class="play-pause-icon">
            <FeatherIcon v-if="isPlaying" name="pause" size="20" color="#fff" />
            <FeatherIcon v-else name="play" size="20" color="#fff" />
          </view>
        </view>
        <view class="music-player-info">
          <text class="music-name">{{ currentMusic.title }}</text>
        </view>
        <view class="music-player-controls">
          <view class="control-btn close-btn" @click="closeMusicPlayer">
            <FeatherIcon name="x" size="20" color="#fff" />
          </view>
        </view>
      </view>
    </view>

    <!-- 消息操作菜单 -->
    <view
      class="message-menu"
      v-if="isMessageMenuVisible"
      :style="messageMenuStyle"
      @tap.stop
    >
      <view class="menu-items">
        <view class="menu-item" @tap="copyMessageContent(currentMessage)">
          <FeatherIcon name="copy" size="20" color="#333" />
          <text>复制</text>
        </view>
        <view class="menu-item" @tap="quoteMessage(currentMessage)">
          <FeatherIcon name="message-square" size="20" color="#333" />
          <text>引用</text>
        </view>
      </view>
    </view>

    <!-- 点击空白处关闭菜单的遮罩 -->
    <view
      class="menu-mask"
      v-if="isMessageMenuVisible"
      @tap="closeMessageMenu"
    ></view>
  </view>
</template>

<script setup>
import {
  ref,
  reactive,
  onMounted,
  nextTick,
  computed,
  onBeforeUnmount,
  onUnmounted,
} from "vue";
import {
  formatDate,
  timeAgo,
  showLoading,
  hideLoading,
  showError,
  showSuccess,
} from "@/utils/common.js";
import api from "@/api";
import { isLoggedIn, getApiKey } from "@/api";
import FeatherIcon from "@/components/FeatherIcon.vue";
import ImagePreview from "@/components/ImagePreview.vue";

// 当前用户名
const currentUser = ref("");
// 当前用户头像
const currentUserAvatar = ref("https://file.fishpi.cn/default-avatar.png");
// 聊天消息列表
const messages = ref([]);
// 是否正在加载
const loading = ref(false);
// 是否有更多消息
const hasMore = ref(true);
// 分页数据
const page = ref(1);
// 是否在发送消息
const sending = ref(false);
// 是否在刷新
const refreshing = ref(false);
// 消息内容
const messageContent = ref("");
// 滚动位置
const scrollTop = ref(0);
// 是否应该自动滚动到底部
const shouldAutoScroll = ref(true);
// 用户是否手动滚动了页面
const userScrolled = ref(false);
// WebSocket连接
let webSocket = null;
// 重连次数
let reconnectCount = 0;
// 最大重连次数
const MAX_RECONNECT = 3; // 增加最大重连次数
// 重连定时器
let reconnectTimer = null;
// 添加活动检测相关变量
let activityTimer = null;
const ACTIVITY_TIMEOUT = 30000; // 30秒无活动视为断开
// 连接状态: 'connecting', 'connected', 'disconnected'
const connectionStatus = ref("disconnected");
// 当前节点名称
const nodeName = ref("");
// 是否自动重连
const autoReconnect = ref(true);

// 表情包相关状态
const showEmojiPanel = ref(false);
const emoticonList = ref([]);
const isLoadingEmoticons = ref(false);
// 新增表情包相关状态
const defaultEmoticons = ref([]); // 默认表情
const customEmoticons = ref([]); // 用户自定义表情
const isLoadingCustomEmoticons = ref(false); // 是否正在加载自定义表情
const activeEmojiTab = ref("default"); // 当前激活的表情tab
const hasLoadedCustomEmoticons = ref(false); // 是否已加载过自定义表情
const selectedEmojiIndex = ref(-1); // 选中要删除的表情索引

// 新增状态变量
const onlineCount = ref(0);
const onlineUsers = ref([]);
const showUserList = ref(false);
const currentTopic = ref("");
// 新增未读消息相关状态
const hasNewMessage = ref(false);
const newMessageCount = ref(0);
let redPacketNoticeTimer = null;
// 在data部分添加新的变量
const showMorePanel = ref(false); // 是否显示功能面板

// 添加相关方法
// 切换功能面板
const toggleMorePanel = () => {
  // 如果功能面板已打开，则关闭它
  if (showMorePanel.value) {
    showMorePanel.value = false;
    // 重置键盘高度
    keyboardHeight.value = 0;
    // 恢复消息列表位置，移除底部边距
    setTimeout(() => {
      scrollToBottom();
    }, 100);
  } else {
    // 如果功能面板未打开，则打开它并关闭表情面板
    showMorePanel.value = true;
    showEmojiPanel.value = false;

    // 调整消息列表位置，增加底部边距避免被遮挡
    // 功能面板高度约为300rpx
    //keyboardHeight.value = 10; // 设置一个模拟的键盘高度，使消息列表上移

    // 延迟滚动到底部确保布局更新
    setTimeout(() => {
      scrollToBottom();
    }, 100);
  }
};
// 选择图片
const chooseImage = () => {
  uni.chooseImage({
    count: 1,
    sizeType: ["compressed"],
    sourceType: ["album"],
    success: async (res) => {
      try {
        showLoading("上传中...");
        const tempFilePath = res.tempFilePaths[0];

        // 上传图片
        const uploadResult = await api.user.uploadImage(tempFilePath);

        if (uploadResult.code === 0) {
          // 获取上传成功的图片URL
          const fileName = tempFilePath.split("/").pop();
          const imageUrl =
            uploadResult.data.succMap[fileName] ||
            uploadResult.data.fileUrl ||
            uploadResult.data.url;

          if (!imageUrl) {
            throw new Error("未获取到图片URL");
          }

          // 插入图片标签到消息内容
          messageContent.value += `<img src="${imageUrl}" alt="图片">`;
          // 关闭功能面板
          showMorePanel.value = false;

          // 可以选择直接发送或等用户编辑后发送
          sendMessage();
        } else {
          showError(uploadResult.msg || "上传失败");
        }
      } catch (error) {
        console.error("上传图片失败:", error);
        showError(error.message || "上传失败");
      } finally {
        hideLoading();
      }
    },
  });
};

// 拍照
const takePhoto = () => {
  uni.chooseImage({
    count: 1,
    sizeType: ["compressed"],
    sourceType: ["camera"],
    success: async (res) => {
      try {
        showLoading("上传中...");
        const tempFilePath = res.tempFilePaths[0];
        // 上传图片
        const uploadResult = await api.user.uploadImage(tempFilePath);
        if (uploadResult.code === 0) {
          // 获取上传成功的图片URL
          const fileName = tempFilePath.split("/").pop();
          const imageUrl =
            uploadResult.data.succMap[fileName] ||
            uploadResult.data.fileUrl ||
            uploadResult.data.url;

          if (!imageUrl) {
            throw new Error("未获取到图片URL");
          }
          // 插入图片标签到消息内容
          messageContent.value += `<img src="${imageUrl}" alt="图片">`;
          // 关闭功能面板
          showMorePanel.value = false;
          // 可以选择直接发送或等用户编辑后发送
          sendMessage();
        } else {
          showError(uploadResult.msg || "上传失败");
        }
      } catch (error) {
        console.error("上传图片失败:", error);
        showError(error.message || "上传失败");
      } finally {
        hideLoading();
      }
    },
  });
};

// 打开红包功能
const openRedPacket = () => {
  uni.navigateTo({
    url: "/pages/chat/send-redpacket",
  });
  showMorePanel.value = false;
};

// 存储消息内容与图片URL的映射关系
const imageUrlsMap = new Map();

// 存储消息图片的映射
const messageImagesMap = reactive(new Map());

// 检查消息是否包含图片
const hasImages = (message) => {
  // 如果是红包消息或红包通知，不需要额外显示图片
  if (
    message._isRedPacket ||
    message._isRedPacketNotice ||
    message._isSystemMessage
  ) {
    return false;
  }

  // 检查消息内容是否包含图片标签
  if (!message.content) return false;

  return message.content.includes("<img") || message.content.includes("![");
};

// 获取消息中的图片列表
const getMessageImages = (message) => {
  if (!message) return [];

  // 先检查缓存
  if (messageImagesMap.has(message.oId)) {
    return messageImagesMap.get(message.oId);
  }

  // 没有缓存则提取图片
  const images = extractImagesFromMessage(message);
  messageImagesMap.set(message.oId, images);
  return images;
};

// 从消息内容中提取图片URL
const extractImagesFromMessage = (message) => {
  if (!message || !message.content) return [];

  const imageUrls = [];
  const imgRegex = /<img[^>]+src="([^"]+)"/g;
  let match;

  while ((match = imgRegex.exec(message.content)) !== null) {
    if (match[1]) {
      imageUrls.push(match[1]);
    }
  }

  return imageUrls;
};

// 预览图片
const previewImage = (currentUrl, urls) => {
  if (!currentUrl) return;

  console.log("预览图片:", currentUrl);
  console.log("图片列表:", urls);

  uni.previewImage({
    urls: urls,
    current: currentUrl,
    indicator: "number",
    loop: true,
    success: () => {
      console.log("图片预览成功");
    },
    fail: (err) => {
      console.error("图片预览失败:", err);
      // 备用方案：直接预览单张图片
      uni.previewImage({
        urls: [currentUrl],
        success: () => console.log("备用预览成功"),
        fail: (e) => console.error("备用预览失败:", e),
      });
    },
  });
};
// 跳转到用户详情页
const navigateToUserDetail = (username) => {
  uni.navigateTo({
    url: `/pages/user/user-profile?username=${username}`,
  });
};
// 渲染消息内容 - 新函数，解决渲染问题
const renderMessageContent = (message, isPlainText = false) => {
  // 优化：使用防御性判断
  if (!message) return "";

  // 红包领取通知直接返回内容
  if (message._isRedPacketNotice) {
    return message.content;
  }

  // 获取消息内容
  const content = message.content || "";
  if (!content) return "";

  try {
    // 检查是否是特殊消息类型
    if (isSpecialMessage(message)) {
      // 如果看起来像JSON，尝试解析
      if (content.trim().startsWith("{") && content.trim().endsWith("}")) {
        try {
          // 使用try-catch解析，避免直接修改原始数据
          const jsonData = JSON.parse(content);

          // 天气消息
          if (jsonData.msgType === "weather" && jsonData.type === "weather") {
            return createWeatherHTML(jsonData);
          }

          // 音乐消息
          if (jsonData.msgType === "music") {
            return createMusicHTML(jsonData);
          }

          // 红包消息
          if (jsonData.msgType === "redPacket") {
            return createRedPacketHTML(jsonData, message.oId);
          }

          // 其他JSON消息
          return `<div class="special-content">${content}</div>`;
        } catch (e) {
          console.error("解析JSON消息失败:", e);
          return `<div class="special-content">${content}</div>`;
        }
      }

      // 非JSON特殊消息
      return `<div class="special-content">${content}</div>`;
    }

    // 普通文本消息，直接返回内容
    return isPlainText ? content : content;
  } catch (error) {
    console.error("渲染消息内容失败:", error);
    return content;
  }
};
const isRedPacketMessage = (message) => {
  if (!message || !message.content) return false;
  try {
    if (
      message.content.trim().startsWith("{") &&
      message.content.trim().endsWith("}")
    ) {
      const jsonData = JSON.parse(message.content);
      return jsonData.msgType === "redPacket";
    }
  } catch (e) {
    return false;
  }
  return false;
};
// 创建天气消息HTML
const createWeatherHTML = (weatherData) => {
  try {
    const dates = weatherData.date ? weatherData.date.split(",") : [];
    const maxTemps = weatherData.max ? weatherData.max.split(",") : [];
    const minTemps = weatherData.min ? weatherData.min.split(",") : [];
    const weatherCodes = weatherData.weatherCode
      ? weatherData.weatherCode.split(",")
      : [];
    const location = weatherData.t || "未知地区";
    const status = weatherData.st || "未知天气";

    let html = `
      <div class="weather-card">
        <div class="weather-header">
          <div class="weather-location">${location}天气</div>
          <div class="weather-status">${status}</div>
        </div>
        <div class="weather-days">`;

    // 添加未来几天天气
    for (let i = 0; i < dates.length && i < 3; i++) {
      const date = dates[i] || `未知`;
      const weatherCode = weatherCodes[i] || "CLOUDY";
      const maxTemp = maxTemps[i] || "?";
      const minTemp = minTemps[i] || "?";

      // 选择天气图标
      let weatherIcon = "☁️"; // 默认阴天
      if (weatherCode.includes("RAIN")) {
        weatherIcon = "🌧️";
      } else if (
        weatherCode.includes("SUNNY") ||
        weatherCode.includes("CLEAR")
      ) {
        weatherIcon = "☀️";
      } else if (
        weatherCode.includes("CLOUDY") &&
        weatherCode.includes("PARTLY")
      ) {
        weatherIcon = "⛅";
      } else if (weatherCode.includes("SNOW")) {
        weatherIcon = "❄️";
      }

      html += `
        <div class="weather-day">
          <div class="weather-date">${date}</div>
          <div class="weather-icon">${weatherIcon}</div>
          <div class="weather-temp">${minTemp}°~${maxTemp}°</div>
        </div>`;
    }

    html += `
        </div>
      </div>`;

    return html;
  } catch (e) {
    console.error("格式化天气消息失败", e);
    return weatherData.t ? `${weatherData.t}天气` : "天气信息";
  }
};

// 创建红包消息HTML - 优化展示效果
const createRedPacketHTML = (redPacketData, oId) => {
  // 使用深拷贝而不是直接修改原始对象
  const packetData = JSON.parse(JSON.stringify(redPacketData));
  console.log("packetData:", packetData);
  // 适配历史消息和实时消息的不同字段格式
  // 判断红包状态
  const isOver =
    packetData.isOver ||
    packetData.leftCount === 0 ||
    (packetData.count && packetData.got && packetData.got === packetData.count);

  // 适配历史消息中的got字段（可能是数字）与实时消息中的hasGot（布尔值）
  const hasGot =
    typeof packetData.hasGot !== "undefined" ? packetData.hasGot : false;

  // 适配totalCount和count
  const totalCount = packetData.totalCount || packetData.count || 1;

  // 适配leftCount和got
  let leftCount;
  if (packetData.leftCount !== undefined) {
    leftCount = packetData.leftCount;
  } else if (typeof packetData.got === "number") {
    // 历史消息中got是已领取数量
    leftCount = Math.max(0, totalCount - packetData.got);
  } else if (packetData.got && Array.isArray(packetData.got)) {
    // 实时消息中got是领取记录数组
    leftCount = Math.max(0, totalCount - packetData.got.length);
  } else if (packetData.who && Array.isArray(packetData.who)) {
    // 历史消息中who是领取记录数组
    leftCount = Math.max(0, totalCount - packetData.who.length);
  } else {
    leftCount = totalCount;
  }

  const redPacketId = oId;
  const redPacketType = packetData.type || "normal";

  // 设置不同状态下的样式
  let statusClass = "";
  let statusText = "";

  if (isOver) {
    statusClass = "red-packet-over";
    statusText = "红包已领完";
  } else if (hasGot) {
    statusClass = "red-packet-got";
    statusText = "已领取";
  } else {
    statusClass = "red-packet-available";
    statusText = `剩余${leftCount}/${totalCount}个`;
  }

  // 红包类型文本
  let typeText = "普通红包";
  if (packetData.type) {
    switch (packetData.type) {
      case "random":
        typeText = "拼手气红包";
        break;
      case "average":
        typeText = "平分红包";
        break;
      case "specify":
        typeText = "专属红包";
        break;
      case "heartbeat":
        typeText = "心跳红包";
        break;
      case "rockPaperScissors":
        typeText = "猜拳红包";
        break;
    }
  }
  // 红包消息的HTML - 使用JavaScript事件
  return `
  <div class="red-packet-card ${statusClass}"
      data-redpacket-id="${redPacketId}"
      data-redpacket-type="${redPacketType}"
      id="redPacket_${redPacketId}"
      style="cursor:pointer;">
    <div class="red-packet-header">
      <div class="red-packet-icon">🧧</div>
      <div class="red-packet-content">
        <div class="red-packet-title">${typeText}</div>
        <div class="red-packet-msg">${
          packetData.msg || "恭喜发财，大吉大利"
        }</div>
        <div class="red-packet-status">${statusText}</div>
      </div>
    </div>
  </div>
`;
};
// 处理红包点击
const handleRedPacketClick = (redPacketId, redPacketType) => {
  console.log("点击了红包:", redPacketId);
  console.log(`尝试打开红包: ${redPacketId}`);

  if (!redPacketId) {
    console.error("未找到红包ID");
    return;
  }
  try {
    // 显示加载中
    showLoading("领取红包中...");

    // 猜拳红包需要额外参数
    if (redPacketType === "rockPaperScissors") {
      // 显示猜拳选择
      hideLoading();
      uni.showActionSheet({
        itemList: ["石头", "剪刀", "布"],
        success: async (res) => {
          const gestures = ["0", "1", "2"];
          const gesture = gestures[res.tapIndex];

          try {
            showLoading("猜拳中...");
            // 抢红包
            const result = await api.chat.getRedPacket(redPacketId, gesture);
            hideLoading();

            // 成功抢到红包，带上结果跳转到详情页
            uni.navigateTo({
              url: `/pages/chat/red-packet-detail?id=${redPacketId}&type=${redPacketType}&data=${encodeURIComponent(
                JSON.stringify(result)
              )}`,
            });
          } catch (error) {
            hideLoading();
            console.error("猜拳红包领取失败:", error);
            showError("领取失败: " + (error.message || "未知错误"));
          }
        },
      });
      return;
    }

    // 领取其他类型红包
    api.chat
      .getRedPacket(redPacketId)
      .then((result) => {
        hideLoading();
        // 成功抢到红包，带上结果跳转到详情页
        uni.navigateTo({
          url: `/pages/chat/red-packet-detail?id=${redPacketId}&type=${redPacketType}&data=${encodeURIComponent(
            JSON.stringify(result)
          )}`,
        });
      })
      .catch((error) => {
        hideLoading();
        console.error("领取红包失败:", error);
        showError("领取失败: " + (error.message || "未知错误"));
      });
  } catch (error) {
    hideLoading();
    console.error("领取红包失败:", error);
    showError("领取失败: " + (error.message || "未知错误"));
  }
};

// 连接状态样式
const connectionStatusClass = computed(() => {
  return {
    connected: connectionStatus.value === "connected",
    connecting: connectionStatus.value === "connecting",
    disconnected: connectionStatus.value === "disconnected",
  };
});

// 连接状态文本
const connectionStatusText = computed(() => {
  switch (connectionStatus.value) {
    case "connected":
      return "已连接";
    case "connecting":
      return "连接中...";
    case "disconnected":
      return "已断开";
    default:
      return "未知";
  }
});

// 获取当前用户信息
const getCurrentUser = async () => {
  if (!isLoggedIn()) return;

  try {
    const res = await api.user.getCurrentUser();

    if (res.code === 0) {
      currentUser.value = res.data.userName;
      // 设置用户头像
      if (res.data.userAvatarURL) {
        currentUserAvatar.value = res.data.userAvatarURL;
      }
    }
  } catch (error) {
    console.error("获取用户信息失败", error);
  }
};

// 初始化WebSocket连接
const initWebSocket = async () => {
  // 先检查连接状态，避免重复连接
  if (
    connectionStatus.value === "connected" ||
    connectionStatus.value === "connecting"
  ) {
    console.log("WebSocket已经处于连接或连接中状态，跳过重复连接");
    return;
  }

  // 断开现有连接
  closeWebSocket();

  connectionStatus.value = "connecting";
  console.log("开始初始化WebSocket连接");

  // 设置连接超时检测
  const connectionTimeout = setTimeout(() => {
    if (connectionStatus.value === "connecting") {
      console.log("WebSocket连接超时，中断连接并尝试重连");
      // 标记为断开
      connectionStatus.value = "disconnected";
      // 尝试强制关闭
      try {
        uni.closeSocket();
      } catch (e) {
        console.log("关闭超时连接时出错", e);
      }
      // 触发重连
      if (autoReconnect.value) {
        handleReconnect();
      }
    }
  }, 10000); // 10秒连接超时

  try {
    // 获取节点地址
    let chatNodeUrl = "";
    let hasError = false;
    console.log("尝试获取聊天节点");
    try {
      try {
        const res = await api.chat.getChatNode();
        console.log("getChatNode:", JSON.stringify(res));
        if (res && res.code === 0 && res.data) {
          chatNodeUrl = res.data;
          nodeName.value = res.msg || "摸鱼派聊天室";
        } else {
          throw new Error("备用方法也无法获取聊天节点");
        }
      } catch (backupError) {
        console.error("获取聊天节点失败，详细错误:", backupError);
        throw new Error("无法获取聊天节点地址，请检查网络连接或重新登录");
      }
    } catch (nodeError) {
      console.error("获取聊天节点失败，详细错误:", nodeError);
      hasError = true;
    }
    if (!chatNodeUrl) {
      throw new Error("获取聊天节点地址失败");
    }
    try {
      // 使用 uni.connectSocket
      uni.connectSocket({
        url: chatNodeUrl,
        success: () => {
          console.log("WebSocket连接已创建，等待连接打开");
          // 注意：这里不要更新状态，因为连接创建不等于连接打开
          // 状态应该在onSocketOpen回调中更新
        },
        fail: (err) => {
          console.error("连接失败", err);
          clearTimeout(connectionTimeout); // 清除超时检测
          handleConnectionError("创建WebSocket连接失败");
        },
        complete: () => {
          // 防止内存泄漏
          setTimeout(() => {
            if (connectionStatus.value === "connecting") {
              console.log("连接过程较长，请等待...");
            }
          }, 3000);
        },
      });

      // 注意：事件监听器已经在onMounted中设置，这里不再重复设置
    } catch (socketError) {
      console.error("创建WebSocket对象失败:", socketError);
      clearTimeout(connectionTimeout); // 清除超时检测
      handleConnectionError(
        "创建WebSocket连接失败: " + (socketError.message || "未知错误")
      );
    }
  } catch (error) {
    console.error("初始化WebSocket连接失败", error);
    clearTimeout(connectionTimeout); // 清除超时检测
    connectionStatus.value = "disconnected";
    showError("连接聊天室失败：" + (error.message || "未知错误"));
    if (autoReconnect.value) {
      setTimeout(() => handleReconnect(), 5000);
    }
  }
};

// 增强错误处理
const handleConnectionError = (errorMsg) => {
  console.error(errorMsg);
  connectionStatus.value = "disconnected";
  handleReconnect();
};

// 修改重连逻辑
const handleReconnect = () => {
  // 检查是否达到最大重连次数
  const MAX_RECONNECT_ATTEMPTS = 5;
  if (reconnectCount >= MAX_RECONNECT_ATTEMPTS) {
    console.log("已达到最大重连次数，停止重连");
    connectionStatus.value = "disconnected";
    autoReconnect.value = false;
    return;
  }
  // 优化重连策略：使用斐波那契数列间隔
  const RECONNECT_DELAYS = [1000, 2000, 3000, 5000, 8000, 13000, 21000];
  const delay =
    RECONNECT_DELAYS[Math.min(reconnectCount, RECONNECT_DELAYS.length - 1)];

  console.log(`将在${delay}ms后尝试第${reconnectCount + 1}次重连`);
  reconnectTimer = setTimeout(() => {
    reconnectCount++; // 增加重连计数
    initWebSocket();
  }, delay);
};

// 手动重连
const manualReconnect = () => {
  // 如果正在连接中，直接返回
  if (connectionStatus.value === "connecting") {
    console.log("正在连接中，请耐心等待");
    return;
  }

  // 清除任何正在进行的重连计时器
  if (reconnectTimer) {
    clearTimeout(reconnectTimer);
    reconnectTimer = null;
  }

  // 重置重连计数
  reconnectCount = 0;

  // 重置连接状态
  connectionStatus.value = "disconnected";

  // 延迟执行，确保状态更新
  setTimeout(() => {
    // 尝试关闭现有连接
    try {
      uni.closeSocket({
        success: () => {
          console.log("已关闭现有连接，准备重新连接");
        },
        fail: () => {
          console.log("无法关闭连接，可能已断开");
        },
        complete: () => {
          // 无论如何，都尝试重新连接
          console.log("开始手动重连");
          initWebSocket();
        },
      });
    } catch (e) {
      console.log("关闭连接出错，直接尝试重连", e);
      // 开始新的连接
      initWebSocket();
    }
  }, 500);
};

// 处理WebSocket接收到的消息
const handleWebSocketMessage = (data) => {
  try {
    // 检查data是否已经有效
    if (!data) {
      console.error("收到无效的WebSocket消息数据:", data);
      return;
    }

    // 处理不同类型的消息
    const messageType = data.type;
    if (!messageType) {
      console.error("收到的消息没有type字段:", data);
      return;
    }

    switch (messageType) {
      case "msg": // 聊天消息
        // 处理聊天消息
        console.log("收到聊天消息:", data);
        handleChatMessage(data);
        break;
      case "online": // 在线用户更新
        handleOnlineUsers(data);
        break;
      case "revoke": // 消息撤回
        // 直接传递整个消息对象
        handleRevokeMessage(data);
        break;
      case "redPacketStatus": // 红包状态
        handleRedPacketStatus(data);
        break;
      case "customMessage": // 自定义消息 默认就是 一句话 谁谁谁来了 之类的
        // handleCustomMessage(data);
        break;
      default:
        console.log("未知消息类型", data);
    }
  } catch (error) {
    console.error("处理WebSocket消息出错", error, "原始数据:", data);
  }
};

// 处理聊天消息
const handleChatMessage = (message) => {
  console.log("处理聊天消息, 当前消息数:", messages.value.length);

  // 检查是否是WebSocket直接推送的消息格式(包含type和data)
  let actualMessage = message;
  if (message.type === "msg" && message.data) {
    console.log("这是WebSocket推送的消息，提取data部分:", message.data);
    actualMessage = message.data;
  }

  // 获取消息的唯一标识
  const messageId =
    actualMessage.oId ||
    (actualMessage.time && new Date(actualMessage.time).getTime());

  if (!messageId) {
    console.error("消息没有有效的标识:", actualMessage);
    return;
  }

  // 检查消息是否已存在 - 只根据oId进行精确匹配
  const existingMessageIndex = messages.value.findIndex(
    (m) => m.oId && m.oId === messageId
  );

  if (existingMessageIndex >= 0) {
    console.log("消息已存在，不重复添加:", messageId);
    return;
  }

  // 在添加新消息前，检查是否需要显示新消息提示
  // 判断是否应该显示通知
  const shouldShowNotification = userScrolled.value && !shouldAutoScroll.value;

  // 使用不可变方式更新数组，避免直接修改响应式对象
  const newMessages = [...messages.value, { ...actualMessage }];

  // 批量更新状态，减少更新次数
  messages.value = newMessages;
  console.log("消息已添加, 当前消息数:", messages.value.length);

  // 判断是否应该滚动到底部或显示新消息提示
  if (shouldAutoScroll.value) {
    // 用户在底部时，自动滚动到底部
    console.log("用户在底部，自动滚动到底部");
    // 延迟调用scrollToBottom，避免在当前更新周期中嵌套更新
    setTimeout(() => {
      scrollToBottom();
      // 确保没有未读消息提示
      hasNewMessage.value = false;
      newMessageCount.value = 0;
    }, 0);
  } else if (shouldShowNotification) {
    // 用户正在查看历史消息，显示新消息提示
    console.log("用户正在查看历史，显示新消息提示");
    hasNewMessage.value = true;
    newMessageCount.value++;
  }
};

// 处理在线用户信息
const handleOnlineUsers = (message) => {
  if (message.onlineChatCnt !== undefined) {
    onlineCount.value = message.onlineChatCnt;
  }

  if (message.users) {
    onlineUsers.value = message.users;
  }

  if (message.discussing) {
    console.log("收到讨论话题", message.discussing);
    currentTopic.value =
      message.discussing !== "暂无" ? message.discussing : "";
  }
};
const onScroll = (e) => {
  // 获取滚动位置
  const scrollTop = e.detail.scrollTop;
  const scrollHeight = e.detail.scrollHeight;

  // 动态查询视图高度
  uni
    .createSelectorQuery()
    .select(".chat-messages") // 使用 class 或 ref
    .boundingClientRect((res) => {
      if (!res) return;

      const viewHeight = res.height; // 获取可视区域高度

      // 判断是否接近底部
      const isNearBottom = scrollHeight - scrollTop - viewHeight < 20;
      if (isNearBottom) {
        // 用户已滚动到底部，设置自动滚动标志，并隐藏新消息提示
        shouldAutoScroll.value = true;
        userScrolled.value = false;
        // 如果有新消息提示，则隐藏
        if (hasNewMessage.value) {
          hasNewMessage.value = false;
          newMessageCount.value = 0;
        }
      } else {
        // 用户主动向上滚动，只有不在底部时才设置这些标志
        shouldAutoScroll.value = false;
        userScrolled.value = true;
      }
    })
    .exec();
};
// 加载历史聊天消息
const loadMessages = async (reset = false) => {
  if (loading.value) return;

  try {
    loading.value = true;

    if (reset) {
      page.value = 1;
      hasMore.value = true;
      // 重置时应该滚动到底部
      shouldAutoScroll.value = true;
      userScrolled.value = false;
    }

    console.log("正在加载第", page.value, "页的消息");
    const res = await api.chat.getMessages(page.value);

    if (res.code === 0) {
      const newMessages = res.data || [];
      console.log(
        "获取到历史消息数量:",
        newMessages.length,
        "当前页码:",
        page.value
      );

      // 对消息进行排序，确保旧消息在上面，新消息在下面
      const sortedMessages = [...newMessages].sort((a, b) => {
        const timeA = new Date(a.time).getTime();
        const timeB = new Date(b.time).getTime();
        return timeA - timeB; // 按时间升序排列
      });

      // 预处理消息，特别是处理红包状态
      const processedMessages = sortedMessages.map((message) => {
        // 尝试解析红包消息
        if (
          message.content &&
          typeof message.content === "string" &&
          message.content.trim().startsWith("{") &&
          message.content.trim().endsWith("}")
        ) {
          try {
            const contentObj = JSON.parse(message.content);

            // 如果是红包消息
            if (contentObj.msgType === "redPacket") {
              // 标记为红包消息
              Object.defineProperty(message, "_isRedPacket", {
                value: true,
                configurable: true,
                enumerable: false,
              });

              // 检查红包是否已领完 - 处理历史消息与实时消息不同的字段结构
              if (
                contentObj.totalCount &&
                contentObj.got &&
                Array.isArray(contentObj.got)
              ) {
                // 实时消息格式：got是数组
                if (contentObj.got.length >= contentObj.totalCount) {
                  contentObj.isOver = true;
                  contentObj.leftCount = 0;
                }
              } else if (
                contentObj.count &&
                typeof contentObj.got === "number"
              ) {
                // 历史消息格式：got是数字，表示已领取数量
                if (contentObj.got >= contentObj.count) {
                  contentObj.isOver = true;
                  contentObj.leftCount = 0;
                } else {
                  contentObj.leftCount = contentObj.count - contentObj.got;
                }
              } else if (
                contentObj.count &&
                contentObj.who &&
                Array.isArray(contentObj.who)
              ) {
                // 历史消息格式：who是数组，表示领取记录
                if (contentObj.who.length >= contentObj.count) {
                  contentObj.isOver = true;
                  contentObj.leftCount = 0;
                } else {
                  contentObj.leftCount =
                    contentObj.count - contentObj.who.length;
                }

                // 将历史消息格式转换为新格式，方便统一处理
                if (!contentObj.totalCount) {
                  contentObj.totalCount = contentObj.count;
                }

                // 转换who数组为got数组格式
                if (!contentObj.got || !Array.isArray(contentObj.got)) {
                  contentObj.got = contentObj.who.map((item) => ({
                    userName: item.userName,
                    userAvatarURL: item.avatar,
                    amount:
                      item.userMoney > 0
                        ? item.userMoney
                        : Math.abs(item.userMoney),
                  }));
                }
              } else if (contentObj.leftCount === 0) {
                contentObj.isOver = true;
              }

              // 更新消息内容
              message.content = JSON.stringify(contentObj);
            }
          } catch (e) {
            console.error("预处理红包消息失败:", e);
          }
        }
        return message;
      });

      if (reset) {
        messages.value = processedMessages;
      } else {
        // 加载更多时，将新消息添加到列表前面
        console.log(
          "合并消息，当前消息数:",
          messages.value.length,
          "新消息数:",
          processedMessages.length
        );
        messages.value = [...processedMessages, ...messages.value];
      }

      // 判断是否还有更多消息
      if (newMessages.length < 20) {
        hasMore.value = false;
      } else {
        // 只有在成功加载到消息时才增加页码
        page.value++;
      }

      // 如果是首次加载或应该自动滚动，滚动到底部
      if (reset || shouldAutoScroll.value) {
        console.log("加载消息后自动滚动到底部");
        await scrollToBottom();
      }
    } else {
      showError(res.msg || "加载聊天记录失败");
    }
  } catch (error) {
    console.error("加载聊天记录失败", error);
  } finally {
    loading.value = false;
  }
};

// 加载更多消息
const loadMoreMessages = async () => {
  if (!hasMore.value || loading.value) return;
  console.log("触发加载更多消息，当前页码:", page.value);
  await loadMessages(false);
};

// 发送消息的方法也需要关闭所有面板
const sendMessage = async () => {
  if (!isLoggedIn()) {
    goToLogin();
    return;
  }

  // 检查是否有消息内容或引用
  const currentQuote =
    (typeof window !== "undefined" && window?._currentQuote) || null;

  if (!messageContent.value.trim() && !currentQuote) {
    return;
  }

  if (connectionStatus.value !== "connected") {
    showError("聊天室连接已断开，请等待重连");
    return;
  }

  try {
    sending.value = true;
    console.log("发送消息:", messageContent.value);

    // 保存当前消息内容
    let content = messageContent.value;

    // 处理引用
    if (currentQuote) {
      console.log("处理引用:", currentQuote);
      const quote = currentQuote;
      const quotePrefix = `\n\n ##### 引用 @${quote.userName} [↩](https://fishpi.cn/cr#chatroom${quote.oId} "跳转至原消息")\n\n> ${quote.content}\n\n`;
      content = content + quotePrefix;

      // 清除引用状态
      if (typeof window !== "undefined") {
        window._currentQuote = null;
      }
      showQuoteIndicator.value = false;
    }

    // 添加小尾巴
    try {
      const tailSettings = uni.getStorageSync("chatTailSettings");
      if (tailSettings) {
        const settings = JSON.parse(tailSettings);
        if (settings.enableTail && settings.tailContent) {
          // 在消息末尾添加小尾巴，使用分隔线
          content += `\n\n >${settings.tailContent}`;
        }
      }
    } catch (e) {
      console.error("读取小尾巴设置失败", e);
    }

    // 先清空输入框，提高响应速度
    messageContent.value = "";

    // 关闭所有面板
    showMorePanel.value = false;
    showEmojiPanel.value = false;
    // 重置模拟键盘高度
    keyboardHeight.value = 0;

    // 标记应该滚动到底部
    shouldAutoScroll.value = true;
    userScrolled.value = false;

    const res = await api.chat.sendMessage(content);
    console.log("发送消息API响应:", res);

    if (res.code === 0) {
      console.log("消息发送成功");

      // 确保滚动到底部
      setTimeout(() => {
        scrollToBottom();
      }, 100);

      // 如果服务器没有通过WebSocket推送消息，手动添加一条本地消息
      if (res.data && typeof res.data === "object") {
        console.log("尝试添加本地消息:", res.data);

        // 构造一个临时消息对象
        const tempMessage = {
          ...res.data,
          userNickname: currentUser.value, // 使用当前用户名
          userName: currentUser.value,
          userAvatarURL:
            res.data.userAvatarURL ||
            "https://file.fishpi.cn/default-avatar.png",
          time: new Date().toISOString(),
          oId: res.data.oId || `temp_${Date.now()}`,
          content: content,
        };

        handleChatMessage(tempMessage);
      }
    } else {
      // 发送失败，恢复消息内容
      messageContent.value = content;
      showError("发送消息失败: " + (res.msg || "未知错误"));
    }
  } catch (error) {
    console.error("发送消息异常:", error);
    showError(error.message || "发送消息失败");
  } finally {
    sending.value = false;
  }
};

// 滚动到底部
const scrollToBottom = async (delay = 100) => {
  console.log("尝试滚动到底部");

  // 强制设置标志位
  shouldAutoScroll.value = true;
  userScrolled.value = false;

  // 使用延迟确保DOM更新完成
  setTimeout(() => {
    try {
      // 创建查询
      const query = uni.createSelectorQuery();
      query.select(".chat-messages").boundingClientRect();
      query.select(".chat-list").boundingClientRect();

      query.exec((res) => {
        if (!res || !res[0] || !res[1]) {
          console.log("滚动查询结果无效:", res);
          // 使用备用方案
          scrollTop.value = 999999;
          return;
        }

        console.log("滚动查询结果:", res);
        const containerHeight = res[0].height;
        const contentHeight = res[1].height;
        // 计算需要滚动的位置，加上额外空间确保滚到底部
        const newScrollTop = contentHeight - containerHeight + 100;
        if (newScrollTop > 0) {
          // 使用更高的值确保滚动到底部
          scrollTop.value = newScrollTop + 300;
        } else {
          scrollTop.value = 999999;
        }
      });
    } catch (error) {
      console.error("滚动尝试失败:", error);
      // 备用方案
      scrollTop.value = 999999;
    }
  }, delay);
};

// 判断是否是自己发送的消息
const isSelfMessage = (message) => {
  return message.userName === currentUser.value;
};

// 判断是否为特殊消息类型（比如红包、天气、系统通知等）
const isSpecialMessage = (message) => {
  // 如果是系统用户或机器人发送的消息
  if (
    message.userOId === 0 ||
    (message.sysMetal &&
      (message.sysMetal.includes("机器人") ||
        message.sysMetal.includes("小冰")))
  ) {
    return true;
  }

  // 检查消息内容
  if (message.content) {
    try {
      // 首先检查内容是否可能是JSON格式
      if (
        typeof message.content === "string" &&
        message.content.trim().startsWith("{") &&
        message.content.trim().endsWith("}") &&
        (message.content.includes('"msgType"') ||
          message.content.includes('\\"msgType\\"'))
      ) {
        // 尝试解析JSON
        let jsonContent;
        try {
          jsonContent = JSON.parse(message.content);
        } catch (e) {
          // 可能是转义的JSON，尝试二次解析
          try {
            const unescaped = message.content.replace(/\\/g, "");
            jsonContent = JSON.parse(unescaped);
          } catch (e2) {
            // 无法解析，仍按关键词判断
          }
        }

        // 如果成功解析为对象，检查msgType
        if (jsonContent && jsonContent.msgType) {
          return true;
        }

        // 关键词判断（兜底方案）
        return (
          message.content.includes("redPacket") ||
          message.content.includes("weather") ||
          message.content.includes("msgType")
        );
      }
    } catch (e) {
      console.log("判断特殊消息时出错", e);
    }
  }

  return false;
};

// 格式化日期头部显示
const formatDateHeader = (timestamp) => {
  if (!timestamp) return "未知日期";

  const messageDate = new Date(timestamp);
  const today = new Date();
  const yesterday = new Date(today);
  yesterday.setDate(today.getDate() - 1);

  // 今天
  if (messageDate.toDateString() === today.toDateString()) {
    return "今天";
  }

  // 昨天
  if (messageDate.toDateString() === yesterday.toDateString()) {
    return "昨天";
  }

  // 其他日期，显示完整日期
  return formatDate(timestamp, "YYYY年MM月DD日");
};

// 格式化消息时间显示
const formatMessageTime = (timestamp) => {
  if (!timestamp) return "";

  return formatDate(timestamp, "HH:mm");
};

// 判断两个时间是否是不同的日期
const isDifferentDay = (time1, time2) => {
  if (!time1 || !time2) return false;

  const date1 = new Date(time1);
  const date2 = new Date(time2);

  return date1.toDateString() !== date2.toDateString();
};

// 跳转到登录页
const goToLogin = () => {
  uni.navigateTo({
    url: "/pages/login/login",
  });
};

// 加载表情包列表
const loadEmoticons = async () => {
  if (isLoadingEmoticons.value) return;

  try {
    isLoadingEmoticons.value = true;
    const res = await api.chat.getEmoticon();

    if (res.code === 0 && res.data) {
      if (Array.isArray(res.data)) {
        emoticonList.value = res.data;
      } else {
        // 如果返回的是JSON字符串，则解析
        try {
          const parsedData = JSON.parse(res.data);
          if (Array.isArray(parsedData)) {
            emoticonList.value = parsedData;
          }
        } catch (e) {
          console.error("解析表情包数据失败", e);
        }
      }
    } else {
      console.error("获取表情包失败", res.msg);
    }
  } catch (error) {
    console.error("加载表情包失败", error);
  } finally {
    isLoadingEmoticons.value = false;
  }
};

// 切换用户列表显示状态
const toggleUserList = () => {
  showUserList.value = !showUserList.value;
};

// 打开用户主页
const openUserHomePage = (url) => {
  if (!url) return;

  // 使用内置浏览器打开链接
  // #ifdef APP-PLUS
  plus.runtime.openURL(url);
  // #endif

  // #ifdef H5
  window.open(url, "_blank");
  // #endif
};

// 处理消息撤回
const handleRevokeMessage = (message) => {
  try {
    console.log("处理撤回消息:", message);

    // 检查消息格式
    const revokeData = message.data || message;

    if (!revokeData || !revokeData.oId) {
      console.error("撤回消息数据格式错误:", message);
      return;
    }

    // 获取要撤回的消息ID
    const revokeId = revokeData.oId;
    console.log("要撤回的消息ID:", revokeId);

    // 删除对应的消息
    const index = messages.value.findIndex((m) => m.oId === revokeId);
    if (index >= 0) {
      console.log("找到要撤回的消息，索引:", index);
      // 直接删除消息
      messages.value.splice(index, 1);
      // 更新消息列表
      messages.value = [...messages.value];
      console.log("已删除撤回的消息");
    } else {
      console.log("未找到要撤回的消息, ID:", revokeId);
    }
  } catch (error) {
    console.error("处理撤回消息出错:", error);
  }
};

// 返回上一页
const goBack = () => {
  uni.navigateBack();
};

// 关闭WebSocket连接
const closeWebSocket = () => {
  console.log("关闭WebSocket连接");
  // 禁用自动重连
  autoReconnect.value = false;
  // 清除重连定时器
  if (reconnectTimer) {
    clearTimeout(reconnectTimer);
    reconnectTimer = null;
  }

  // 只有在已连接状态时才尝试关闭WebSocket
  if (connectionStatus.value === "connected") {
    try {
      uni.closeSocket({
        success: () => {
          console.log("WebSocket连接已成功关闭");
          connectionStatus.value = "disconnected";
        },
        fail: (err) => {
          console.log("关闭WebSocket连接失败，可能已经断开", err);
          connectionStatus.value = "disconnected";
        },
      });
    } catch (error) {
      console.error("关闭WebSocket时出错", error);
      connectionStatus.value = "disconnected";
    }
  } else {
    console.log("WebSocket连接已处于断开状态，无需再次关闭");
    connectionStatus.value = "disconnected";
  }
};

// 取消WebSocket事件监听
const removeSocketListeners = () => {
  console.log("尝试移除WebSocket事件监听器");
  try {
    // 检查方法是否存在并添加兼容性处理
    if (typeof uni.offSocketOpen === "function") {
      uni.offSocketOpen();
    }
    if (typeof uni.offSocketClose === "function") {
      uni.offSocketClose();
    }
    if (typeof uni.offSocketMessage === "function") {
      uni.offSocketMessage();
    }
    if (typeof uni.offSocketError === "function") {
      uni.offSocketError();
    }

    // 兼容性方法 - 某些旧版本需要传入回调函数
    try {
      uni.offSocketOpen(function () {});
      uni.offSocketClose(function () {});
      uni.offSocketMessage(function () {});
      uni.offSocketError(function () {});
    } catch (e) {
      console.log("尝试备用方法移除WebSocket事件监听器");
    }

    console.log("WebSocket事件监听器移除成功");
  } catch (error) {
    console.error("移除WebSocket事件监听器出错", error);
    console.log("继续执行，忽略移除事件监听器错误");
    // 不抛出异常，允许继续执行
  }
};

// 处理消息气泡点击
const handleMessageBubbleTap = (event, message) => {
  // 阻止事件冒泡
  event.stopPropagation();

  try {
    // 获取点击位置
    const { x, y } = event.detail;

    // 使用uni.createSelectorQuery获取点击位置的元素
    const query = uni.createSelectorQuery();
    query
      .selectAll(".music-card")
      .boundingClientRect((data) => {
        if (data) {
          // 查找点击位置所在的音乐卡片
          const clickedCard = data.find((card) => {
            return (
              x >= card.left &&
              x <= card.right &&
              y >= card.top &&
              y <= card.bottom
            );
          });

          if (clickedCard) {
            const musicId = clickedCard.id;
            const source = clickedCard.dataset.source;
            const title = clickedCard.dataset.musicTitle;
            const coverURL = clickedCard.dataset.musicCover;

            if (musicId && source) {
              const musicInfo = {
                id: musicId,
                title: title || "音乐分享",
                coverURL:
                  coverURL || "https://file.fishpi.cn/default-music-cover.png",
                source: source,
              };

              // 直接调用组件内的方法
              toggleMusicPlayer(musicInfo);
              return;
            }
          }
        }
      })
      .exec();
    // 查找红包卡片
    query
      .selectAll(".red-packet-card")
      .boundingClientRect((data) => {
        if (data && data.length > 0) {
          // 查找点击位置所在的红包卡片
          const clickedRedPacket = data.find((card) => {
            return (
              x >= card.left &&
              x <= card.right &&
              y >= card.top &&
              y <= card.bottom
            );
          });
          if (clickedRedPacket) {
            const redPacketId = clickedRedPacket.dataset.redpacketId;
            const redPacketType = clickedRedPacket.dataset.redpacketType;
            console.log("找到红包点击:", redPacketId, redPacketType);
            if (redPacketId) {
              // 调用红包处理函数
              handleRedPacketClick(redPacketId, redPacketType);
              return;
            }
          }
        }
      })
      .exec();
  } catch (error) {
    console.error("处理消息气泡点击错误:", error);
  }
};

// 监听组件挂载
onMounted(async () => {
  console.log("聊天页面已加载");
  // 初始化全局对象，用于音乐播放和红包处理
  if (typeof window !== "undefined") {
    window.__vue_app = {
      toggleMusicPlayer: toggleMusicPlayer,
    };
  }

  if (isLoggedIn()) {
    try {
      // 加载当前用户信息
      getCurrentUser();
      // 加载表情包
      loadEmoticons();
      // 初始化WebSocket连接
      initWebSocket();
      // 创建音频上下文
      createAudioContext();
    } catch (error) {
      console.error("初始化聊天功能失败:", error);
    }
  } else {
    console.log("用户未登录，跳过初始化聊天功能");
  }
});

// 初始化时先重置连接状态
connectionStatus.value = "disconnected";

// 设置WebSocket事件监听器
uni.onSocketOpen(() => {
  console.log("WebSocket连接已打开");
  connectionStatus.value = "connected";
  // 重置重连计数
  reconnectCount = 0;
  // 加载最近消息
  loadMessages();
});

uni.onSocketMessage((res) => {
  try {
    const data = JSON.parse(res.data);
    handleWebSocketMessage(data);
  } catch (error) {
    console.error("处理WebSocket消息出错:", error);
  }
});

uni.onSocketClose(() => {
  console.log("WebSocket连接已关闭");
  // 更新连接状态
  connectionStatus.value = "disconnected";
  // 如果设置了自动重连，则尝试重连
  if (autoReconnect.value) {
    handleReconnect();
  }
});

uni.onSocketError(() => {
  // 处理连接错误
  handleConnectionError("WebSocket连接出错");
});
onBeforeUnmount(() => {
  console.log("聊天页面即将卸载，清理资源");
  try {
    // 关闭WebSocket连接
    closeWebSocket();
    // 移除事件监听，即使出错也不中断卸载流程
    try {
      removeSocketListeners();
    } catch (e) {
      console.error("移除监听器失败，但继续卸载", e);
    }
  } catch (error) {
    console.error("页面卸载清理出错", error);
  }
});

// 长按用户头像@用户
const atUser = (message) => {
  if (!message || !message.userName) return;

  // 在输入框中添加@用户名
  messageContent.value += `@${message.userName} `;

  // 聚焦输入框
  setTimeout(() => {
    const inputElement = document.querySelector(".message-input");
    if (inputElement) {
      inputElement.focus();
    }
  }, 100);
};

// 显示消息操作菜单
const showMessageMenu = (message) => {
  try {
    // 获取当前点击的消息元素位置
    const query = uni.createSelectorQuery();
    query
      .select(`#msg-${message.oId || ""}`)
      .boundingClientRect(function (res) {
        if (res) {
          // 计算菜单位置，确保显示在消息的上方
          const menuTop = res.top - 60; // 减小距离，更靠近消息卡片

          // 根据消息发送者调整菜单位置
          let menuLeft;
          const isSelf = message.userName === currentUser.value?.userName;
          console.log(
            "isSelf:",
            isSelf,
            "message.userName:",
            message.userName,
            "currentUser:",
            currentUser.value?.userName
          );

          if (isSelf) {
            // 自己发送的消息，菜单右对齐
            menuLeft = res.left + res.width - 300; // 300是菜单的宽度
          } else {
            // 他人发送的消息，菜单左对齐
            menuLeft = res.left + 40;
          }

          // 设置菜单位置样式
          messageMenuStyle.value = {
            top: `${menuTop}px`,
            left: `${menuLeft}px`,
          };

          // 显示菜单
          currentMessage.value = message;
          isMessageMenuVisible.value = true;
        }
      })
      .exec();
  } catch (error) {
    console.error("显示消息菜单失败:", error);
  }
};

// 关闭消息菜单
const closeMessageMenu = () => {
  isMessageMenuVisible.value = false;
  currentMessage.value = null;
};

// 复制消息内容
const copyMessageContent = (message) => {
  try {
    let content = "";
    // 提取纯文本内容
    if (message.content) {
      // 先将HTML转换为纯文本
      content = message.content.replace(/<[^>]*>/g, "");
      // 过滤掉小尾巴内容（通常在blockquote中或在 > 后面）
      // 1. 移除所有以 > 开头的行
      content = content
        .split("\n")
        .filter((line) => !line.trim().startsWith(">"))
        .join("\n");
      // 2. 如果内容中包含换行符加空行的模式（通常是小尾巴的前缀），截取到这个位置之前的内容
      const tailMarkerIndex = content.indexOf("\n\n");
      if (tailMarkerIndex > 0) {
        content = content.substring(0, tailMarkerIndex);
      }
    }
    // 复制到剪贴板
    uni.setClipboardData({
      data: content,
      success: () => {
        uni.showToast({
          title: "复制成功",
          icon: "success",
        });
        // 关闭消息菜单
        closeMessageMenu();
      },
    });
  } catch (error) {
    console.error("复制消息内容失败", error);
    // 出错时也关闭菜单
    closeMessageMenu();
  }
};

// 修改引用格式
const quoteMessage = (message) => {
  if (!message) return;

  try {
    // 提取用户名
    const userName = message.userName || message.userNickname || "用户";
    // 提取消息内容 - 移除HTML标签
    let content = "";
    if (message.content) {
      content = message.content.replace(/<[^>]*>/g, "");
      // 限制长度
      if (content.length > 30) {
        content = content.substring(0, 30) + "...";
      }
    }
    // 为此消息保存引用信息
    if (typeof window !== "undefined") {
      window._currentQuote = {
        userName: userName,
        content: content,
        oId: message.oId || "",
        originalMessage: message,
      };
    }
    // 显示引用提示
    uni.showToast({
      title: `引用: @${userName}`,
      icon: "none",
      duration: 2000,
    });
    // 更新引用状态指示
    showQuoteIndicator.value = true;
    // 聚焦输入框
    setTimeout(() => {
      if (typeof document !== "undefined") {
        const inputElement = document.querySelector(".message-input");
        if (inputElement) {
          inputElement.focus();
        }
      }
    }, 100);
    // 关闭消息菜单
    closeMessageMenu();
  } catch (error) {
    console.error("引用消息失败", error);
    closeMessageMenu();
  }
};

// 添加引用状态标记
const showQuoteIndicator = ref(false);

// 取消引用
const cancelQuote = () => {
  if (typeof window !== "undefined") {
    window._currentQuote = null;
  }
  showQuoteIndicator.value = false;
};

// 添加红包状态处理函数
const handleRedPacketStatus = (data) => {
  try {
    console.log("处理红包状态:", data);

    // 处理直接传入的红包状态数据
    const statusData = data.data || data;
    if (!statusData) {
      console.error("红包状态数据格式错误:", data);
      return;
    }
    // 更新特定红包消息的状态
    updateRedPacketMessage(statusData);
  } catch (error) {
    console.error("处理红包状态出错:", error);
  }
};

// 更新红包消息状态
const updateRedPacketMessage = (statusData) => {
  if (!statusData.oId && !statusData.msgId) {
    return;
  }

  // 获取红包消息ID
  const redPacketMsgId = statusData.msgId || statusData.oId;

  // 查找消息列表中对应的红包消息
  const msgIndex = messages.value.findIndex(
    (msg) => msg.oId === redPacketMsgId
  );

  if (msgIndex === -1) {
    return;
  }

  // 更新红包状态
  try {
    const targetMsg = messages.value[msgIndex];
    if (
      targetMsg.content &&
      typeof targetMsg.content === "string" &&
      targetMsg.content.trim().startsWith("{") &&
      targetMsg.content.trim().endsWith("}")
    ) {
      const redPacketData = JSON.parse(targetMsg.content);
      if (redPacketData.msgType !== "redPacket") {
        return;
      }

      // 更新红包状态 - 不使用响应式方式
      const updatedData = { ...redPacketData };

      // 更新领取记录
      if (statusData.got) {
        updatedData.got = statusData.got;
      } else if (statusData.whoGot) {
        if (!updatedData.got) updatedData.got = [];
        const hasUser = updatedData.got.some(
          (item) => item.userName === statusData.whoGot
        );
        if (!hasUser) {
          updatedData.got.push({
            userName: statusData.whoGot,
            userAvatarURL:
              statusData.userAvatarURL20 || statusData.userAvatarURL || "",
            amount: statusData.userMoney || 0,
          });
        }
      }

      // 更新数量
      if (statusData.totalCount) {
        updatedData.totalCount = statusData.totalCount;
      }
      if (statusData.leftCount !== undefined) {
        updatedData.leftCount = statusData.leftCount;
      } else if (statusData.count) {
        updatedData.leftCount = Math.max(
          0,
          (updatedData.totalCount || 1) - statusData.count
        );
      }

      // 更新状态
      if (statusData.hasGot !== undefined) {
        updatedData.hasGot = statusData.hasGot;
      } else if (statusData.whoGot === currentUser.value) {
        updatedData.hasGot = true;
      }

      // 更新是否领完的状态 - 判断leftCount是否为0或got长度是否等于totalCount
      if (
        updatedData.leftCount === 0 ||
        (updatedData.got &&
          updatedData.got.length >= (updatedData.totalCount || 1))
      ) {
        updatedData.isOver = true;
      } else {
        updatedData.isOver = false;
      }

      // 更新消息内容
      targetMsg.content = JSON.stringify(updatedData);

      // 强制清除渲染缓存
      delete targetMsg._renderedContent;

      // 刷新消息列表
      messages.value = [...messages.value];
    }
  } catch (error) {
    console.error("更新红包消息失败:", error);
  }
};

// 将处理函数注入到window作用域
if (typeof window !== "undefined") {
  window.handleRedPacketClick = function (redPacketId, redPacketType) {
    console.log(`全局红包点击处理: ${redPacketId}, 类型: ${redPacketType}`);
    // 确保参数类型一致
    redPacketId = String(redPacketId);
    // 直接调用handleRedPacketClick函数
    handleRedPacketClick(redPacketId, redPacketType);
  };
}

// 点击新消息通知跳转到底部
const goToLatestMessage = () => {
  console.log("点击新消息通知，跳转到底部");
  // 设置自动滚动标志
  shouldAutoScroll.value = true;
  userScrolled.value = false;
  // 滚动到底部
  scrollToBottom();
  // 清除新消息通知
  hasNewMessage.value = false;
  newMessageCount.value = 0;
};
// 创建音乐卡片HTML
const createMusicHTML = (musicData) => {
  try {
    const coverURL =
      musicData.coverURL || "https://file.fishpi.cn/default-music-cover.png";
    const title = musicData.title || "音乐分享";
    const source = musicData.source || "";

    // 创建唯一ID，用于音乐播放器操作
    const musicId = `music_${Date.now()}${Math.floor(Math.random() * 1000)}`;

    // 存储音乐信息，用于悬浮播放器
    if (typeof window !== "undefined") {
      window.__currentMusic = {
        id: musicId,
        title: title,
        coverURL: coverURL,
        source: source,
      };
    }

    // 音乐卡片HTML
    return `
      <div class="music-card" id="${musicId}" data-source="${source}" data-music-id="${musicId}" data-music-title="${title}" data-music-cover="${coverURL}">
        <div class="music-cover">
          <img src="${coverURL}" alt="${title}" />
          <div class="play-icon">▶</div>
        </div>
        <div class="music-info">
          <div class="music-title">${title}</div>
          <div class="music-subtitle">点击播放</div>
        </div>
      </div>
    `;
  } catch (e) {
    console.error("创建音乐卡片失败", e);
    return "音乐分享";
  }
};

// 添加全局音乐点击处理函数
if (typeof window !== "undefined") {
  window.musicClickHandler = function (musicId) {
    console.log("音乐点击处理:", musicId);
    if (
      typeof window.__vue_app !== "undefined" &&
      window.__vue_app.toggleMusicPlayer
    ) {
      // 从当前音乐卡片获取数据
      const musicCard = document.getElementById(musicId);
      if (musicCard) {
        const source = musicCard.getAttribute("data-source");
        const titleElement = musicCard.querySelector(".music-title");
        const coverElement = musicCard.querySelector("img");

        const musicInfo = {
          id: musicId,
          title: titleElement ? titleElement.innerText : "音乐分享",
          coverURL: coverElement
            ? coverElement.src
            : "https://file.fishpi.cn/default-music-cover.png",
          source: source || "",
        };

        console.log("准备播放音乐:", musicInfo);
        // 调用播放方法
        window.__vue_app.toggleMusicPlayer(musicInfo);
      } else {
        console.error("未找到音乐卡片元素:", musicId);
      }
    } else {
      console.error("音乐播放器组件未初始化");
    }
  };
}

// 音乐播放器状态
const showMusicPlayer = ref(false);
const isPlaying = ref(false);
const currentMusic = ref({
  id: "",
  title: "",
  coverURL: "",
  source: "",
});

// 音频上下文引用
let audioContext = null;

// 创建音频上下文
const createAudioContext = () => {
  // 创建内部音频上下文
  audioContext = uni.createInnerAudioContext();

  // 设置事件监听
  audioContext.onEnded(() => {
    handleMusicEnded();
  });

  audioContext.onError((res) => {
    console.error("音频播放错误", res);
    handleMusicError();
  });
};

// 显示/隐藏音乐播放器
const toggleMusicPlayer = (musicInfo) => {
  if (!musicInfo) return;

  // 更新音乐信息
  currentMusic.value = {
    id: musicInfo.id,
    title: musicInfo.title,
    coverURL: musicInfo.coverURL,
    source: musicInfo.source,
  };

  // 显示播放器
  showMusicPlayer.value = true;

  // 设置音频源
  if (audioContext) {
    audioContext.stop();
    audioContext.src = musicInfo.source;
  } else {
    createAudioContext();
    audioContext.src = musicInfo.source;
  }
};

// 切换播放/暂停
const toggleMusicPlayback = () => {
  if (!audioContext) {
    createAudioContext();
    audioContext.src = currentMusic.value.source;
  }

  if (isPlaying.value) {
    audioContext.pause();
    isPlaying.value = false;
  } else {
    audioContext.play();
    isPlaying.value = true;

    // 监听播放失败
    audioContext.onError(() => {
      console.error("播放音乐失败");
      isPlaying.value = false;
      uni.showToast({
        title: "播放失败，请重试",
        icon: "none",
      });
    });
  }
};

// 关闭音乐播放器
const closeMusicPlayer = () => {
  if (audioContext) {
    audioContext.stop();
  }
  isPlaying.value = false;
  showMusicPlayer.value = false;
};

// 处理音乐播放结束
const handleMusicEnded = () => {
  isPlaying.value = false;
};

// 处理音乐播放错误
const handleMusicError = () => {
  isPlaying.value = false;
  uni.showToast({
    title: "音乐播放失败",
    icon: "none",
  });
};

// 组件销毁前清理
onBeforeUnmount(() => {
  if (audioContext) {
    audioContext.destroy();
    audioContext = null;
  }
});

// 软键盘高度
const keyboardHeight = ref(0);
// 添加commentFocus变量
const commentFocus = ref(false);

// 处理键盘事件需要更新，确保键盘收起时关闭所有面板
const handleInputFocus = () => {
  // 输入框聚焦时关闭所有面板
  showEmojiPanel.value = false;
  showMorePanel.value = false;
  commentFocus.value = true;

  // 延迟滚动到底部，确保布局更新后
  setTimeout(() => {
    scrollToBottom();
  }, 100);
};

// 处理输入框失焦事件
const handleInputBlur = () => {
  // 延迟滚动到底部，确保键盘收起后再滚动
  setTimeout(() => {
    scrollToBottom(300);
  }, 300);
};

// 监听键盘高度变化
// #ifdef APP-PLUS
uni.onKeyboardHeightChange((res) => {
  console.log("键盘高度变化：", res.height);

  // 先更新高度
  keyboardHeight.value = res.height;

  // 延迟执行滚动，确保布局完成后
  setTimeout(() => {
    if (res.height > 0) {
      // 键盘弹出时，立即滚动到底部
      scrollToBottom();
    } else {
      // 键盘收起时，重置状态并滚动到底部
      commentFocus.value = false;
      scrollToBottom();
    }
  }, 200); // 增加延迟时间，确保布局更新完成
});
// #endif
// 组件卸载时移除监听
onUnmounted(() => {
  // #ifdef APP-PLUS
  uni.offKeyboardHeightChange();
  // #endif
});

// 消息菜单相关
const isMessageMenuVisible = ref(false);
const messageMenuStyle = ref({});
const currentMessage = ref(null);

// 处理自定义消息
const handleCustomMessage = (data) => {
  // 创建新的消息对象，使用普通文本而不是响应式对象
  const customMessage = {
    oId: Date.now().toString(),
    type: "text", // 改为text类型，避免使用RichText组件
    content: data.message,
    time: new Date().toISOString(),
    userName: "系统",
    userAvatar: "/static/fishPiLogo.png",
    isCustomMessage: true, // 添加标记，用于模板中区分显示方式
  };

  // 使用nextTick确保在下一个更新周期添加消息
  nextTick(() => {
    messages.value = [...messages.value, customMessage];

    // 如果用户当前在底部，自动滚动到最新消息
    if (shouldAutoScroll.value) {
      scrollToBottom();
    }
  });
};
// 切换表情面板显示状态
const toggleEmojiPanel = () => {
  // 如果表情面板已打开，则关闭它
  if (showEmojiPanel.value) {
    showEmojiPanel.value = false;
    // 重置键盘高度
    keyboardHeight.value = 0;
    // 恢复消息列表位置，移除底部边距
    setTimeout(() => {
      scrollToBottom();
    }, 100);
  } else {
    // 如果表情面板未打开，则打开它并关闭功能面板
    showEmojiPanel.value = true;
    showMorePanel.value = false;

    // 调整消息列表位置，增加底部边距避免被遮挡
    // 表情面板高度大约为500rpx
    //keyboardHeight.value = 10; // 设置一个模拟的键盘高度，使消息列表上移

    // 延迟滚动到底部确保布局更新
    setTimeout(() => {
      scrollToBottom();
    }, 100);

    // 如果打开表情面板且尚未加载表情包，则加载表情包
    if (
      activeEmojiTab.value === "default" &&
      defaultEmoticons.value.length === 0
    ) {
      loadDefaultEmoticons();
    } else if (
      activeEmojiTab.value === "custom" &&
      !hasLoadedCustomEmoticons.value
    ) {
      loadCustomEmoticons();
    }
  }
};

// 切换到默认表情页
const switchToDefaultTab = () => {
  activeEmojiTab.value = "default";
  selectedEmojiIndex.value = -1; // 重置删除状态
  if (defaultEmoticons.value.length === 0 && !isLoadingEmoticons.value) {
    loadDefaultEmoticons();
  }
};

// 切换到自定义表情页
const switchToCustomTab = () => {
  activeEmojiTab.value = "custom";
  selectedEmojiIndex.value = -1; // 重置删除状态
  if (!hasLoadedCustomEmoticons.value && !isLoadingCustomEmoticons.value) {
    loadCustomEmoticons();
  }
};

// 处理表情标签切换
const handleEmojiTabChange = (e) => {
  const current = e.detail.current;
  activeEmojiTab.value = current === 0 ? "default" : "custom";

  // 如果切换到自定义表情标签且未加载过
  if (
    current === 1 &&
    !hasLoadedCustomEmoticons.value &&
    !isLoadingCustomEmoticons.value
  ) {
    loadCustomEmoticons();
  }
};

// 加载默认表情
const loadDefaultEmoticons = async () => {
  if (isLoadingEmoticons.value) return;

  try {
    isLoadingEmoticons.value = true;
    // 加载默认表情
    const defaultRes = await api.chat.getDefaultEmoticon();
    if (defaultRes.code === 0) {
      // 处理特殊格式的默认表情包
      if (Array.isArray(defaultRes.data)) {
        // 检查是否是每个元素都是一个对象，且对象只有一个键值对的格式
        const firstItem = defaultRes.data[0];
        if (
          firstItem &&
          typeof firstItem === "object" &&
          Object.keys(firstItem).length === 1
        ) {
          // 特殊格式：[{smile: "😄"}, {flushed: "😳"}, ...]
          // 将其转换为表情符号数组
          defaultEmoticons.value = defaultRes.data.map((item) => {
            const key = Object.keys(item)[0];
            return item[key];
          });
        } else {
          // 普通数组格式，直接使用
          defaultEmoticons.value = defaultRes.data;
        }
      } else if (defaultRes.data && typeof defaultRes.data === "object") {
        // 如果是对象格式，尝试提取emotions字段或其他数组字段
        defaultEmoticons.value =
          defaultRes.data.emotions ||
          Object.values(defaultRes.data).find((val) => Array.isArray(val)) ||
          [];
      } else {
        defaultEmoticons.value = [];
      }
    }
  } catch (error) {
    console.error("加载默认表情包失败", error);
    defaultEmoticons.value = [];
  } finally {
    isLoadingEmoticons.value = false;
  }
};

// 加载自定义表情
const loadCustomEmoticons = async () => {
  if (isLoadingCustomEmoticons.value) return;

  try {
    isLoadingCustomEmoticons.value = true;
    // 加载用户表情包
    const customRes = await api.chat.getEmoticon();
    if (customRes.code === 0) {
      if (Array.isArray(customRes.data)) {
        // 检查是否是与默认表情包相同的特殊格式
        const firstItem = customRes.data[0];
        if (
          firstItem &&
          typeof firstItem === "object" &&
          Object.keys(firstItem).length === 1
        ) {
          // 特殊格式，将其转换为表情符号或URL数组
          customEmoticons.value = customRes.data
            .map((item) => {
              const key = Object.keys(item)[0];
              return item[key];
            })
            .reverse(); // 反转顺序，最新的在前面
        } else {
          // 普通数组格式
          customEmoticons.value = [...customRes.data].reverse(); // 反转顺序，最新的在前面
        }
      } else if (
        customRes.data &&
        typeof customRes.data === "string" &&
        customRes.data.trim()
      ) {
        try {
          const parsedData = JSON.parse(customRes.data);
          if (Array.isArray(parsedData)) {
            // 同样检查特殊格式
            const firstItem = parsedData[0];
            if (
              firstItem &&
              typeof firstItem === "object" &&
              Object.keys(firstItem).length === 1
            ) {
              customEmoticons.value = parsedData
                .map((item) => {
                  const key = Object.keys(item)[0];
                  return item[key];
                })
                .reverse(); // 反转顺序，最新的在前面
            } else {
              customEmoticons.value = [...parsedData]; // 已经是反转过的，保持原样
            }
          } else if (parsedData && typeof parsedData === "object") {
            // 尝试从对象中提取数组
            const possibleArray = Object.values(parsedData).find((val) =>
              Array.isArray(val)
            );
            if (possibleArray) {
              customEmoticons.value = [...possibleArray].reverse(); // 反转顺序，最新的在前面
            } else {
              console.log("表情包数据格式不是数组:", parsedData);
              customEmoticons.value = [];
            }
          } else {
            console.log("表情包数据格式不正确:", parsedData);
            customEmoticons.value = [];
          }
        } catch (e) {
          console.error("解析表情包数据失败", e);
          console.log("原始数据:", customRes.data);
          customEmoticons.value = [];
        }
      } else {
        console.log("表情包数据为空或非法格式");
        customEmoticons.value = [];
      }
    } else {
      console.log("获取表情包失败:", customRes.msg);
      customEmoticons.value = [];
    }
    hasLoadedCustomEmoticons.value = true;
  } catch (error) {
    console.error("加载自定义表情包失败", error);
    customEmoticons.value = [];
  } finally {
    isLoadingCustomEmoticons.value = false;
  }
};

// 添加自定义表情
const addCustomEmoji = () => {
  uni.chooseImage({
    count: 1, // 默认9
    sizeType: ["compressed"], // 可以指定是原图还是压缩图，默认二者都有
    sourceType: ["album", "camera"], // 从相册选择或拍照
    success: async (res) => {
      try {
        showLoading("上传中...");
        const tempFilePath = res.tempFilePaths[0];

        // 上传图片
        const uploadResult = await api.user.uploadImage(tempFilePath);

        if (uploadResult.code === 0) {
          // 获取文件名和上传成功的图片URL
          const fileName = tempFilePath.split("/").pop();
          // 获取上传成功的图片URL
          const imageUrl =
            uploadResult.data.succMap[fileName] ||
            uploadResult.data.fileUrl ||
            uploadResult.data.url;

          if (!imageUrl) {
            throw new Error("未获取到图片URL");
          }

          // 向自定义表情列表添加新表情 - 添加到数组前面，而不是末尾
          const newCustomEmoticons = [imageUrl, ...customEmoticons.value];

          // 同步到服务器
          await api.chat.syncEmoticon(
            "emojis",
            JSON.stringify(newCustomEmoticons)
          );

          // 更新本地表情列表
          customEmoticons.value = newCustomEmoticons;

          showSuccess("添加表情成功");
        } else {
          showError(uploadResult.msg || "上传失败");
        }
      } catch (error) {
        console.error("添加表情失败:", error);
        showError(error.message || "添加表情失败");
      } finally {
        hideLoading();
      }
    },
  });
};

// 插入表情到输入框
const insertEmoji = (emojiUrl) => {
  // 检查是否是图片URL
  const isImg = isImageUrl(emojiUrl);
  if (isImg) {
    // 如果是图片URL，使用img标签
    messageContent.value += `<img src="${emojiUrl}" alt="图片表情"">`;
  } else {
    // 普通emoji直接包在p标签内
    messageContent.value += `<p>${emojiUrl}</p>`;
  }
  // 发送消息
  sendMessage();
  // 关闭表情面板
  showEmojiPanel.value = false;
  // 重置键盘高度
  keyboardHeight.value = 0;
  // 滚动到底部
  setTimeout(() => {
    scrollToBottom();
  }, 100);
};

// 长按表情触发删除模式
const handleLongPress = (index) => {
  // 只允许删除自定义表情
  if (activeEmojiTab.value === "custom") {
    selectedEmojiIndex.value = index;
  }
};

// 删除表情
const deleteEmoji = async (index) => {
  try {
    uni.showModal({
      title: "删除表情",
      content: "确定要删除这个表情吗？",
      success: async (res) => {
        if (res.confirm) {
          showLoading("删除中...");

          // 复制一份表情数组并移除选中项
          const newCustomEmoticons = [...customEmoticons.value];
          newCustomEmoticons.splice(index, 1);

          // 同步到服务器
          await api.chat.syncEmoticon(
            "emojis",
            JSON.stringify(newCustomEmoticons)
          );

          // 更新本地表情列表
          customEmoticons.value = newCustomEmoticons;

          // 重置选中状态
          selectedEmojiIndex.value = -1;

          showSuccess("删除成功");
        } else {
          // 取消删除
          selectedEmojiIndex.value = -1;
        }
      },
    });
  } catch (error) {
    console.error("删除表情失败:", error);
    showError(error.message || "删除失败");
    selectedEmojiIndex.value = -1;
  } finally {
    hideLoading();
  }
};

// 判断是否为图片URL
const isImageUrl = (emoji) => {
  if (!emoji || typeof emoji !== "string") return false;
  return (
    emoji.startsWith("http") &&
    (emoji.endsWith(".png") ||
      emoji.endsWith(".jpg") ||
      emoji.endsWith(".jpeg") ||
      emoji.endsWith(".gif") ||
      emoji.endsWith(".webp") ||
      emoji.includes(".png?") ||
      emoji.includes(".jpg?") ||
      emoji.includes(".jpeg?") ||
      emoji.includes(".gif?") ||
      emoji.includes(".webp?"))
  );
};

// 打开小尾巴设置页面
const openTail = () => {
  uni.navigateTo({
    url: "/pages/chat/tail-setting",
  });
  showMorePanel.value = false;
};
</script>

<style lang="scss">
.redpacket-notice-msg {
  text-align: center;
  margin: 10rpx 0;
  color: #ff4d4f;
  font-size: 24rpx;
  background-color: rgba(255, 242, 240, 0.5);
  padding: 6rpx 20rpx;
  border-radius: 30rpx;
  display: inline-block;
  max-width: 80%;
  align-self: center;
}
.chat-container {
  display: flex;
  flex-direction: column;
  height: 100vh;
  background-color: #f5f7fa;
  position: relative;
}

/* 顶部导航栏 - 固定定位 */
.chat-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10rpx 30rpx;
  height: 90rpx;
  background-color: #fff;
  border-bottom: 1px solid rgba(0, 0, 0, 0.05);
  box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.03);
  z-index: 100;
  padding-top: 50rpx;

  .header-left {
    display: flex;
    align-items: center;

    .back-btn {
      width: 60rpx;
      height: 60rpx;
      display: flex;
      align-items: center;
      justify-content: center;
      margin-right: 20rpx;
      border-radius: 50%;

      &:active {
        background-color: #f0f0f0;
      }
    }

    .node-name {
      font-size: 32rpx;
      font-weight: 600;
      color: #333;
    }
  }

  .connection-badge {
    font-size: 24rpx;
    padding: 6rpx 16rpx;
    border-radius: 32rpx;
    display: flex;
    align-items: center;
    justify-content: center;

    &.connected {
      background-color: #ecf9ee;
      color: #42b983;
    }

    &.connecting {
      background-color: #f0f9ff;
      color: #1890ff;
    }

    &.disconnected {
      background-color: #fff0f0;
      color: #ff4d4f;
      cursor: pointer;

      &:active {
        opacity: 0.8;
      }
    }

    FeatherIcon {
      margin-left: 6rpx;
    }
  }

  .online-count {
    font-size: 24rpx;
    padding: 6rpx 16rpx;
    border-radius: 32rpx;
    background-color: #f5f5f5;
    color: #666;
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-left: 20rpx;
  }
}

/* 聊天内容区域 - 添加顶部和底部内边距 */
.chat-content {
  flex: 1;
  margin-top: 150rpx;
  margin-bottom: 150rpx; /* 基础底部边距 */
  position: relative;
  height: calc(100vh - 330rpx); /* 基础高度计算 */
  overflow: hidden;
}

/* 聊天消息区域 */
.chat-messages {
  height: 100%;
  width: 100%;
  position: relative;
  padding: 0; /* 移除padding */
  overflow-y: auto;
}

.chat-list {
  display: flex;
  flex-direction: column;
  padding-left: 30rpx; /* 左侧内边距移到这里 */
  padding-right: 30rpx; /* 右侧内边距移到这里 */
  padding-bottom: 30rpx;
  min-height: 100%;
}

/* 日期分割线 */
.date-divider {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 30rpx 0;
  width: 100%; /* 确保宽度100% */

  text {
    background-color: rgba(0, 0, 0, 0.1);
    color: #666;
    font-size: 24rpx;
    padding: 6rpx 20rpx;
    border-radius: 16rpx;
  }
}

/* 消息项 */
.message-item {
  display: flex;
  margin-bottom: 35rpx;
  position: relative;
  animation: fadeIn 0.3s ease-out;

  @keyframes fadeIn {
    from {
      opacity: 0;
      transform: translateY(10rpx);
    }
    to {
      opacity: 1;
      transform: translateY(0);
    }
  }

  &.self-message {
    flex-direction: row-reverse;

    .avatar-container {
      margin-right: 0;
      margin-left: 16rpx;
    }

    .message-content {
      align-items: flex-end;
      margin-right: 0;

      .message-bubble {
        background-color: #007aff;
        border-radius: 20rpx 12rpx 12rpx 20rpx;
        color: #fff;
        box-shadow: 0 4rpx 12rpx rgba(0, 122, 255, 0.15);

        a {
          color: #fff;
          text-decoration: underline;
        }
      }

      .special-message {
        background-color: #f5f5f5;
        color: #333;
      }
    }
  }
}

/* 头像 */
.avatar-container {
  margin-right: 16rpx;
  align-self: flex-start;

  .avatar {
    width: 80rpx;
    height: 80rpx;
    border-radius: 50%;
    background-color: #f0f0f0;
    object-fit: cover;
    border: 1px solid rgba(0, 0, 0, 0.05);
  }
}

/* 消息内容 */
.message-content {
  display: flex;
  flex-direction: column;
  max-width: 60%;
  min-width: 100rpx;

  .username {
    font-size: 24rpx;
    color: #999;
    margin-bottom: 8rpx;
  }

  .message-bubble {
    padding: 16rpx 20rpx;
    background-color: #fff;
    border-radius: 12rpx 20rpx 20rpx 12rpx;
    box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
    position: relative;
    word-break: break-word;
  }
}

/* 用户回复样式 - 区分于引用部分 */
.user-reply {
  margin-top: 10rpx;
  font-size: 28rpx;
  line-height: 1.5;
  color: #ffffff;
}

/* @用户后面的内容样式 */
.at-user-content {
  color: #333;
  font-weight: normal;
}

/* 引用消息样式 */
.quote-message {
  margin: 10rpx 0;
  border-left: 4rpx solid #1890ff;
  padding-left: 16rpx;
  background-color: rgba(24, 144, 255, 0.05);
  border-radius: 0 8rpx 8rpx 0;
}
/* 自己发送的消息特殊样式 */
.self-message .message-bubble .message-footer {
  border-top: 1px solid rgba(255, 255, 255, 0.2);
  color: rgba(255, 255, 255, 0.8);

  .client-info {
    color: rgba(255, 255, 255, 0.9);
  }
}

/* 消息状态 */
.message-status {
  font-size: 20rpx;
  color: #999;
  padding: 6rpx 10rpx;
  margin-right: 10rpx;
  align-self: flex-end;
  margin-bottom: 16rpx;
}

/* 红包样式 */
.red-packet {
  display: flex;
  align-items: center;
  background: linear-gradient(135deg, #ff5555, #ff0000);
  color: #fff;
  padding: 20rpx;
  border-radius: 16rpx;
  box-shadow: 0 6rpx 15rpx rgba(255, 0, 0, 0.2);
  animation: pulse 2s infinite;

  .red-packet-icon {
    font-size: 40rpx;
    margin-right: 16rpx;
  }

  .red-packet-info {
    flex: 1;

    .red-packet-msg {
      font-size: 28rpx;
      margin-bottom: 8rpx;
    }

    .red-packet-value {
      font-size: 24rpx;
      opacity: 0.9;
    }
  }

  @keyframes pulse {
    0% {
      transform: scale(1);
    }
    50% {
      transform: scale(1.05);
    }
    100% {
      transform: scale(1);
    }
  }
}

/* 加载更多 */
.loading-more {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 20rpx 0;
  height: 60rpx;
  width: 100%; /* 确保宽度100% */

  .loading-dot {
    width: 12rpx;
    height: 12rpx;
    background-color: #007aff;
    border-radius: 50%;
    margin: 0 8rpx;
    animation: loading 1.4s infinite ease-in-out both;

    &:nth-child(1) {
      animation-delay: -0.32s;
    }

    &:nth-child(2) {
      animation-delay: -0.16s;
    }
  }

  @keyframes loading {
    0%,
    80%,
    100% {
      transform: scale(0);
    }
    40% {
      transform: scale(1);
    }
  }
}

/* 没有更多消息提示 */
.no-more {
  text-align: center;
  padding: 20rpx 0;
  color: #999;
  font-size: 24rpx;
  width: 100%; /* 确保宽度100% */
}

/* 空状态 */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 100rpx 0;
  width: 100%; /* 确保宽度100% */

  .empty-icon {
    width: 200rpx;
    height: 200rpx;
    margin-bottom: 30rpx;
  }

  .empty-text {
    font-size: 28rpx;
    color: #999;
  }
}

/* 输入区域 - 固定在底部，避开TabBar */
.input-area {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background-color: #fff;
  padding: 20rpx 30rpx;
  border-top: 1px solid rgba(0, 0, 0, 0.05);
  z-index: 100;
  box-shadow: 0 -4rpx 10rpx rgba(0, 0, 0, 0.03);

  /* 输入框上方的话题 */
  .input-topic {
    padding: 12rpx 20rpx;
    margin-bottom: 15rpx;
    font-size: 24rpx;
    color: #1890ff;
    background-color: #f0f9ff;
    border-radius: 8rpx;
    text-align: center;
    display: flex;
    align-items: center;
    justify-content: center;

    FeatherIcon {
      margin-right: 6rpx;
      flex-shrink: 0;
    }

    text {
      display: block;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
  }

  /* 样式部分 */
  .input-wrapper {
    display: flex;
    align-items: center;
  }

  .emoji-btn {
    width: 70rpx;
    height: 70rpx;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-right: 10rpx;
  }

  .message-input {
    flex: 1;
    height: 80rpx;
    background-color: #f5f5f5;
    border-radius: 40rpx;
    padding: 0 30rpx;
    font-size: 28rpx;
    color: #333;
  }

  .action-btn {
    width: 80rpx;
    height: 80rpx;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-left: 20rpx;
    background-color: #f5f5f5;
    transition: all 0.3s;
  }

  .action-btn:active {
    opacity: 0.8;
  }

  /* 输入框有内容时的发送按钮样式 */
  input:not(:placeholder-shown) + .action-btn,
  .message-input:not(:placeholder-shown) ~ .action-btn {
    background-color: #007aff !important;
  }

  /* 功能面板样式 */
  .more-panel {
    display: flex;
    flex-wrap: wrap;

    padding: 30rpx 20rpx;
    margin-top: 20rpx;
    background-color: #fff;
    border-top: 1px solid rgba(0, 0, 0, 0.05);
  }

  .more-item {
    width: 20%;
    display: flex;
    flex-direction: column;
    align-items: center;
    margin-bottom: 30rpx;
  }

  .more-icon {
    width: 100rpx;
    height: 100rpx;
    border-radius: 20rpx;
    background-color: #f5f5f5;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 10rpx;
  }

  .more-text {
    font-size: 24rpx;
    color: #666;
  }

  /* 表情面板 */
  .emoji-panel {
    background-color: #fff;
    height: 500rpx;
    margin-top: 20rpx;
    border-top: 1px solid rgba(0, 0, 0, 0.05);
    display: flex;
    flex-direction: column;
  }

  .emoji-tabs {
    display: flex;
    justify-content: space-around;
    padding: 10rpx 0;
  }

  .emoji-tab {
    padding: 10rpx 30rpx;
    cursor: pointer;
    font-size: 28rpx;
    color: #666;
    position: relative;
    transition: all 0.3s;
  }

  .active-tab {
    color: #0072ff;
    font-weight: 500;
  }

  .active-tab::after {
    content: "";
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 60rpx;
    height: 4rpx;
    background-color: #0072ff;
    border-radius: 4rpx;
  }

  .emoji-swiper {
    flex: 1;
    height: 200rpx;
  }

  .emoji-scroll {
    height: 100%;
    padding: 10rpx;
  }

  .emoji-list {
    display: flex;
    flex-wrap: wrap;
    padding: 10rpx;
  }

  .emoji-item {
    width: 100rpx;
    height: 100rpx;
    margin: 10rpx;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
  }

  .emoji-image {
    width: 80rpx;
    height: 80rpx;
    object-fit: contain;
  }

  .emoji-text {
    font-size: 40rpx;
    line-height: 80rpx;
    text-align: center;
  }

  .emoji-loading {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background-color: rgba(255, 255, 255, 0.8);
    color: #999;
    font-size: 28rpx;
  }

  .emoji-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 60rpx 0;
    color: #999;
    font-size: 28rpx;
    height: 100%;
  }

  .custom-empty {
    padding-top: 100rpx;
  }

  .add-emoji-btn {
    margin-top: 30rpx;
    background-color: #0072ff;
    color: #fff;
    font-size: 26rpx;
    padding: 12rpx 30rpx;
    border-radius: 30rpx;
    border: none;
  }

  .add-emoji-item {
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .add-emoji-icon {
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .emoji-delete-active {
    opacity: 0.5;
  }

  .emoji-delete-icon {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background-color: rgba(255, 255, 255, 0.8);
    border-radius: 50%;
    width: 60rpx;
    height: 60rpx;
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 10rpx;
    box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.2);
    z-index: 10;
  }
}

/* 未登录提示 - 调整底部位置 */
.login-prompt {
  position: fixed;
  bottom: 100rpx; /* 调整位置，避开TabBar */
  left: 0;
  right: 0;
  z-index: 100;
  padding: 30rpx;
  background-color: #fff;
  border-top: 1px solid rgba(0, 0, 0, 0.05);

  .login-content {
    height: 80rpx;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: #f5f5f5;
    border-radius: 40rpx;
    color: #007aff;
    font-size: 28rpx;
  }
}

/* 在线用户列表弹窗 */
.user-list-modal {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.5);
  z-index: 1000;

  .user-list-container {
    position: absolute;
    top: 140rpx; /* 调整为与header高度+padding-top一致 */
    left: 0;
    right: 0;
    background-color: #fff;
    padding: 20rpx;
    border-radius: 0 0 20rpx 20rpx;
    box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.1);

    .user-list-header {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 10rpx 0 20rpx;

      .user-list-title {
        font-size: 28rpx;
        font-weight: 600;
        color: #333;
      }

      .online-close-btn {
        width: 60rpx;
        height: 60rpx;
        display: flex;
        align-items: center;
        justify-content: center;
      }
    }

    .user-grid {
      display: flex;
      flex-wrap: wrap;
      padding: 10rpx 0 20rpx;

      .user-avatar-item {
        margin: 10rpx;
        width: 80rpx;
        height: 80rpx;

        &:active {
          opacity: 0.8;
        }

        .user-avatar {
          width: 100%;
          height: 100%;
          border-radius: 50%;
          object-fit: cover;
        }
      }
    }
  }
}

/* 天气消息样式 */
.weather-message {
  display: flex;
  flex-direction: column;
  background: linear-gradient(135deg, #70c1ff, #5c95ff);
  color: #fff;
  padding: 20rpx;
  border-radius: 16rpx;
  box-shadow: 0 6rpx 15rpx rgba(92, 149, 255, 0.2);
  margin: 10rpx 0;

  .weather-header {
    margin-bottom: 15rpx;

    .weather-title {
      font-size: 30rpx;
      font-weight: bold;
      margin-bottom: 6rpx;
    }

    .weather-status {
      font-size: 24rpx;
      opacity: 0.9;
    }
  }

  .weather-content {
    display: flex;
    justify-content: space-between;

    .weather-day {
      display: flex;
      flex-direction: column;
      align-items: center;
      padding: 10rpx;
      flex: 1;

      .weather-date {
        font-size: 24rpx;
        margin-bottom: 6rpx;
      }

      .weather-icon {
        font-size: 40rpx;
        margin: 10rpx 0;
      }

      .weather-temp {
        font-size: 24rpx;
      }
    }
  }
}

/* 新消息提示 */
.new-message-alert {
  position: fixed;
  bottom: 220rpx;
  left: 50%;
  transform: translateX(-50%);
  background-color: #007aff;
  color: #fff;
  padding: 10rpx 20rpx;
  border-radius: 30rpx;
  box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.2);
  z-index: 99;
  animation: bounce 0.5s;
  display: flex;
  align-items: center;
  cursor: pointer;
}

.alert-content {
  display: flex;
  align-items: center;
}

.alert-icon {
  margin-right: 10rpx;
}

@keyframes bounce {
  0%,
  20%,
  50%,
  80%,
  100% {
    transform: translateX(-50%) translateY(0);
  }
  40% {
    transform: translateX(-50%) translateY(-10rpx);
  }
  60% {
    transform: translateX(-50%) translateY(-5rpx);
  }
}

/* 消息气泡 */
.message-bubble {
  position: relative;
  word-break: break-word;

  .message-images {
    display: flex;
    flex-wrap: wrap;
    margin-top: 10rpx;

    .message-image-wrapper {
      margin: 10rpx 0;
      display: inline-block;
      border-radius: 8rpx;
      overflow: hidden;
    }

    .message-image {
      max-width: 50%;
      object-fit: contain;
      border-radius: 8rpx;
      background-color: rgba(0, 0, 0, 0.02);
      box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
    }

    .message-footer {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-top: 10rpx;
      border-top: 1px solid rgba(0, 0, 0, 0.05);
      padding-top: 6rpx;
      font-size: 20rpx;
      color: #999;
    }

    .message-time {
      margin-right: 10rpx;
    }

    .client-info {
      color: #1890ff;
      max-width: 200rpx;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
  }
}

/* 天气消息样式 */
.weather-card {
  background: linear-gradient(135deg, #70c1ff, #5c95ff);
  color: #fff;
  border-radius: 12rpx;
  overflow: hidden;
  box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.1);
}

.weather-header {
  padding: 16rpx 20rpx;
  border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}

.weather-location {
  font-size: 28rpx;
  font-weight: bold;
}

.weather-status {
  font-size: 24rpx;
  opacity: 0.9;
}

.weather-days {
  display: flex;
  justify-content: space-around;
  padding: 16rpx 0;
}

.weather-day {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex: 1;
  padding: 10rpx;
}

.weather-date {
  font-size: 24rpx;
  margin-bottom: 10rpx;
}

.weather-icon {
  font-size: 36rpx;
  margin-bottom: 10rpx;
}

.weather-temp {
  font-size: 24rpx;
}
code {
  padding: 0.2em 0.4em;
  margin: 0;
  font-size: 85%;
  border-radius: 3px;
  word-break: break-word;
  background-size: 20px 20px;
  white-space: pre-wrap;
  background-color: rgba(27, 31, 35, 0.05);
}
/* 红包样式 */
.red-packet-card {
  background: linear-gradient(135deg, #ff5555, #ff0000);
  color: #fff;
  padding: 20rpx;
  border-radius: 12rpx;
  display: flex;
  flex-direction: column;
  box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.1);
  position: relative;
  overflow: hidden;
}

.red-packet-header {
  display: flex;
  align-items: center;
}

.red-packet-icon {
  font-size: 48rpx;
  margin-right: 16rpx;
  flex-shrink: 0;
}

.red-packet-content {
  flex: 1;
}

.red-packet-title {
  font-size: 26rpx;
  margin-bottom: 4rpx;
  opacity: 0.9;
}

.red-packet-msg {
  font-size: 28rpx;
  margin-bottom: 6rpx;
  font-weight: bold;
}

.red-packet-status {
  font-size: 24rpx;
  opacity: 0.85;
  margin-top: 4rpx;
}

.red-packet-value {
  font-size: 24rpx;
  opacity: 0.9;
}

/* 红包状态样式 */
.red-packet-over {
  background: linear-gradient(135deg, #cc4444, #aa0000);
  opacity: 0.8;
}

.red-packet-over::after {
  content: "已领完";
  position: absolute;
  right: 10rpx;
  top: 10rpx;
  background-color: rgba(255, 255, 255, 0.3);
  color: #fff;
  padding: 2rpx 10rpx;
  border-radius: 10rpx;
  font-size: 20rpx;
  /* transform: rotate(15deg); */
}

.red-packet-got {
  background: linear-gradient(135deg, #ff7777, #dd0000);
}

.red-packet-got::after {
  content: "已领取";
  position: absolute;
  right: 10rpx;
  top: 10rpx;
  background-color: rgba(255, 255, 255, 0.3);
  color: #fff;
  padding: 2rpx 10rpx;
  border-radius: 10rpx;
  font-size: 20rpx;
}

.red-packet-available {
  animation: pulse 2s infinite;
}

@keyframes pulse {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.02);
  }
  100% {
    transform: scale(1);
  }
}

/* 红包领取记录 */
.red-packet-receivers {
  margin-top: 12rpx;
  padding-top: 8rpx;
  border-top: 1px solid rgba(255, 255, 255, 0.2);
}

.receiver-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 4rpx 0;
  font-size: 22rpx;
}

.receiver-name {
  color: rgba(255, 255, 255, 0.9);
  max-width: 200rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.receiver-amount {
  font-weight: bold;
}

.receiver-more {
  font-size: 20rpx;
  margin-top: 4rpx;
  opacity: 0.9;
  text-align: center;
}

/* 特殊消息样式调整 */
.special-message .weather-card,
.special-message .red-packet-card {
  margin: 0;
  box-shadow: none;
}

/* 消息气泡 */
.message-bubble {
  padding: 16rpx 20rpx;
  background-color: #fff;
  border-radius: 12rpx 20rpx 20rpx 12rpx;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
  position: relative;
  word-break: break-word;
}

/* 引用消息样式 */
.quote-message {
  margin: 10rpx 0;
  border-left: 4rpx solid #1890ff;
  padding-left: 16rpx;
  background-color: rgba(24, 144, 255, 0.05);
  border-radius: 0 8rpx 8rpx 0;
}
blockquote {
  font-size: 24rpx;
  padding-left: 12rpx;
  color: #6a737d;
  border-left: 4rpx solid #eaecef;
}
.quote-header {
  font-size: 24rpx;
  color: #1890ff;
  margin-bottom: 6rpx;
  padding: 6rpx 0;
}

.quote-title {
  font-weight: bold;
}

.quote-extra {
  margin-left: 8rpx;
  opacity: 0.8;
}

.quote-content {
  font-size: 24rpx;
  color: #666;
  padding: 0 0 6rpx 0;
}

/* @用户样式 */
.at-user {
  color: #1890ff;
  font-weight: 500;
}
.at-user-content {
  color: #ffffff;
  font-weight: 500;
}
/* 引用指示器 */
.quote-indicator {
  background-color: #f0f9ff;
  color: #1890ff;
  font-size: 24rpx;
  padding: 12rpx 20rpx;
  margin-bottom: 12rpx;
  border-radius: 8rpx;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.quote-text {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.quote-cancel {
  padding: 6rpx;
  margin-left: 10rpx;
}

.red-packet-notice {
  display: inline-block;
  background-color: rgba(255, 242, 240, 0.8);
  color: #ff4d4f;
  padding: 8rpx 16rpx;
  border-radius: 8rpx;
  margin: 10rpx auto;
  font-size: 24rpx;
  line-height: 1.5;
  text-align: center;
  box-shadow: 0 2rpx 6rpx rgba(255, 77, 79, 0.1);
  max-width: 90%;
}

.user-highlight {
  font-weight: bold;
  color: #ff4d4f;
}

/* 确保红包卡片样式在消息气泡外部也能正常显示 */
.message-content .red-packet-card {
  margin: 0;
  display: inline-block;
  width: 400rpx;
}

code {
  padding: 0.2em 0.4em;
  margin: 0;
  font-size: 85%;
  border-radius: 3px;
  word-break: break-word;
  background-size: 20px 20px;
  white-space: pre-wrap;
  background-color: rgba(27, 31, 35, 0.05);
}

/* 红包通知样式 */
.redpacket-notification {
  background-color: rgba(255, 242, 240, 0.9);
  color: #ff4d4f;
  padding: 12rpx 20rpx;
  border-radius: 8rpx;
  margin: 0 20rpx 12rpx;
  text-align: center;
  box-shadow: 0 2rpx 8rpx rgba(255, 77, 79, 0.15);
  animation: fadeInOut 5s ease-in-out;
  font-size: 26rpx;
}

@keyframes fadeInOut {
  0% {
    opacity: 0;
    transform: translateY(10px);
  }
  10% {
    opacity: 1;
    transform: translateY(0);
  }
  90% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

.red-packet-notice {
  display: inline-block;
  background-color: rgba(255, 242, 240, 0.8);
  color: #ff4d4f;
  padding: 8rpx 16rpx;
  border-radius: 8rpx;
  margin: 10rpx auto;
  font-size: 24rpx;
  line-height: 1.5;
  text-align: center;
  box-shadow: 0 2rpx 6rpx rgba(255, 77, 79, 0.1);
}

.normal-message,
.special-message {
  word-break: break-word;

  img {
    max-width: 100%;
    max-height: 400rpx;
    object-fit: contain;
    margin: 6rpx 0;
    border-radius: 6rpx;
  }
}

/* 系统消息样式 */
.message-item ._isSystemMessage {
  text-align: center;
  margin: 10rpx 0;
  width: 100%;
}

.message-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 10rpx;
  border-top: 1px solid rgba(0, 0, 0, 0.05);
  padding-top: 6rpx;
  font-size: 20rpx;
  color: #999;
}

.message-time {
  margin-right: 10rpx;
}

.client-info {
  color: #1890ff;
  max-width: 200rpx;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 系统消息样式 */
.message-item rich-text {
  max-width: 100%;
}

.message-item ._isSystemMessage {
  text-align: center !important;
  margin: 10rpx auto !important;
  width: 100% !important;
}

/* 系统消息和红包消息 */
._isSystemMessage,
._isRedPacket {
  text-align: center;
  width: 100% !important;
  display: flex;
  justify-content: center;
  margin: 10rpx 0;
}

.red-packet-notice {
  display: inline-block;
  background-color: rgba(255, 242, 240, 0.8);
  color: #ff4d4f;
  padding: 8rpx 16rpx;
  border-radius: 8rpx;
  margin: 10rpx auto;
  font-size: 24rpx;
  line-height: 1.5;
  text-align: center;
  box-shadow: 0 2rpx 6rpx rgba(255, 77, 79, 0.1);
  max-width: 90%;
}

.redpacket-notice-msg {
  text-align: center;
  margin: 10rpx 0;
  color: #ff4d4f;
  font-size: 24rpx;
  background-color: rgba(255, 242, 240, 0.5);
  padding: 6rpx 20rpx;
  border-radius: 30rpx;
  display: inline-block;
  max-width: 80%;
  align-self: center;
}

.music-player-container {
  display: flex;
  align-items: center;
}

.cover-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.music-player-info {
  flex: 1;
}

.music-name {
  font-size: 28rpx;
  color: #333;
}

.music-player-controls {
  display: flex;
  align-items: center;
}

.close-btn {
  margin-left: 10rpx;
}

/* 音乐卡片样式 */
.music-card {
  display: flex;
  background: linear-gradient(135deg, #1976d2, #64b5f6);
  border-radius: 16rpx;
  padding: 20rpx;
  margin: 10rpx 0;
  color: #fff;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
  max-width: 600rpx;
  cursor: pointer;
}

.music-cover {
  width: 120rpx;
  height: 120rpx;
  border-radius: 10rpx;
  overflow: hidden;
  position: relative;
  margin-right: 20rpx;
}

.music-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.play-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 50rpx;
  height: 50rpx;
  background-color: rgba(0, 0, 0, 0.5);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 24rpx;
}

.music-info {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.music-title {
  font-size: 28rpx;
  font-weight: 500;
  margin-bottom: 10rpx;
  color: #fff;
}

.music-subtitle {
  font-size: 24rpx;
  color: rgba(255, 255, 255, 0.8);
}

/* 悬浮音乐播放器 */
.floating-music-player {
  position: fixed;
  bottom: 750rpx;
  right: 30rpx;
  z-index: 1000;
  width: 120rpx;
  border-radius: 60rpx;
  box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.2);
  overflow: visible;
}

.music-player-container {
  position: relative;
  width: 120rpx;
  height: 120rpx;
  background-color: rgba(0, 0, 0, 0.7);
  border-radius: 60rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}

.music-player-cover {
  width: 100rpx;
  height: 100rpx;
  border-radius: 50rpx;
  overflow: hidden;
  position: relative;
}

.cover-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.3s ease;
}

.cover-image.rotating {
  animation: rotate 8s linear infinite;
}

@keyframes rotate {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

.play-pause-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 40rpx;
  height: 40rpx;
  background-color: rgba(0, 0, 0, 0.5);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.music-player-info {
  display: none;
}

.music-player-controls {
  position: absolute;
  top: -10rpx;
  right: -10rpx;
}

.control-btn {
  width: 30rpx;
  height: 30rpx;
  background-color: rgba(0, 0, 0, 0.8);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* 红包领取结果弹窗 */
.red-packet-result {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
}

.result-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.7);
}

.result-container {
  position: relative;
  width: 80%;
  max-width: 600rpx;
  background: linear-gradient(135deg, #ff5555, #ff0000);
  border-radius: 20rpx;
  overflow: hidden;
  box-shadow: 0 8rpx 30rpx rgba(255, 0, 0, 0.3);
  z-index: 1001;
  animation: result-popup 0.4s ease-out;
}

@keyframes result-popup {
  from {
    transform: scale(0.8);
    opacity: 0;
  }
  to {
    transform: scale(1);
    opacity: 1;
  }
}

.result-header {
  display: flex;
  align-items: center;
  padding: 30rpx;
  background-color: rgba(0, 0, 0, 0.2);
  position: relative;
}

.result-avatar {
  width: 80rpx;
  height: 80rpx;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.6);
  margin-right: 20rpx;
}

.result-title {
  flex: 1;
  display: flex;
  flex-direction: column;
}

.result-username {
  font-size: 30rpx;
  color: #fff;
  font-weight: bold;
  margin-bottom: 8rpx;
}

.result-message {
  font-size: 24rpx;
  color: rgba(255, 255, 255, 0.9);
}

.close-btn {
  position: absolute;
  top: -20rpx;
  right: 10rpx;
  width: 40rpx;
  height: 40rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: rgba(0, 0, 0, 0.3);
  border-radius: 50%;
}

.result-content {
  padding: 40rpx 30rpx;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.result-amount {
  display: flex;
  align-items: baseline;
  margin-bottom: 10rpx;
}

.amount-value {
  font-size: 80rpx;
  color: #fff;
  font-weight: bold;
}

.amount-unit {
  font-size: 28rpx;
  color: #fff;
  margin-left: 10rpx;
}

.result-status {
  font-size: 26rpx;
  color: rgba(255, 255, 255, 0.9);
  margin-top: 20rpx;
  padding: 10rpx 30rpx;
  background-color: rgba(0, 0, 0, 0.2);
  border-radius: 30rpx;
}

.result-records {
  background-color: #fff;
  padding: 30rpx;
  border-radius: 0 0 20rpx 20rpx;
}

.records-title {
  font-size: 28rpx;
  color: #333;
  font-weight: bold;
  margin-bottom: 20rpx;
  padding-bottom: 10rpx;
  border-bottom: 1px solid #eee;
}

.record-list {
  max-height: 300rpx;
  overflow-y: auto;
}

.record-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16rpx 0;
  border-bottom: 1px solid #f5f5f5;
}

.record-user {
  display: flex;
  align-items: center;
  flex: 1;
}

.record-avatar {
  width: 50rpx;
  height: 50rpx;
  border-radius: 50%;
  margin-right: 10rpx;
}

.record-username {
  font-size: 26rpx;
  color: #333;
}

.record-time {
  font-size: 24rpx;
  color: #999;
  margin: 0 10rpx;
}

.record-amount {
  font-size: 28rpx;
  color: #ff5555;
  font-weight: bold;
}

/* 消息操作菜单样式 */
.message-menu {
  position: fixed;
  background-color: #fff;
  border-radius: 8rpx;
  box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15);
  padding: 10rpx;
  z-index: 1000;
  width: auto;
  min-width: 300rpx;
  max-width: 500rpx;
  animation: slideDown 0.2s ease-out;
}

.message-menu .menu-items {
  display: flex;
  justify-content: space-around;
  align-items: center;
}

.message-menu .menu-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 10rpx 20rpx;
  font-size: 24rpx;
  color: #333;
  transition: all 0.2s;
  border-radius: 6rpx;
}

.message-menu .menu-item:active {
  background-color: #f5f5f5;
}

.message-menu .menu-item FeatherIcon {
  margin-bottom: 4rpx;
}

@keyframes slideDown {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* 消息操作菜单容器 */
.message-menu-container {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 999;
  background-color: rgba(0, 0, 0, 0.1);
}

/* 菜单遮罩 */
.menu-mask {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 999;
}
</style>
